docs: set daily reliability and spline roadmap

This commit is contained in:
2026-09-14 13:04:26 +02:00
parent c111f85692
commit 584bf303e9
10 changed files with 1435 additions and 28 deletions
+26
View File
@@ -0,0 +1,26 @@
# VernierCAD phased next steps — SDD ledger
Plan: `docs/superpowers/plans/2026-09-14-phased-next-steps.md`
Base: `c111f856929f6c6370cb4ff5cd094bdb39f699f2`
Branch: `codex/phased-next-steps`
| Task | Status | Implementer | Commit | Review | Evidence |
|---|---|---|---|---|---|
| 1. Validate/install baseline | done | controller | operational | verified | full gate exit 0 (1139.501 summed step seconds); staged installed-worker drives pass cross-process; staged and managed native readbacks pass; current c111f856, previous 8dfd8a4 |
| 2. Refresh roadmap | done | task02_roadmap | this commit | self-reviewed; independent review pending | 2026-09-14 authority; release/current/previous evidence reconciled; focused `rg` and `git diff --check` pass |
| 3. Preview generations | pending | — | — | pending | — |
| 4. Imported readiness | pending | — | — | pending | — |
| 5. Saved views/sections | pending | — | — | pending | — |
| 6. Exact spline paths | pending | — | — | pending | — |
| 7. Sweep GUI lifecycle | pending | — | — | pending | — |
| 8. Closed spline profiles | pending | — | — | pending | — |
| 9. Spline loft provenance | pending | — | — | pending | — |
| 10. L3 rod holder | pending | — | — | pending | — |
| 11. Performance measurement | pending | — | — | pending | — |
| 12. CI/software renderer | pending | — | — | pending | — |
| 13. C++ sanitizer evidence | pending | — | — | pending | — |
| 14. Vendored static OCCT | pending | — | — | pending | — |
| 15. AppImage | pending | — | — | pending | — |
| 16. Integrated verification | pending | controller | — | pending | — |
The MikroB dashboard was unavailable at `localhost:3420`; this branch-local ledger is the execution authority. No dashboard database is mutated directly.
+196
View File
@@ -0,0 +1,196 @@
# Task 2 implementation report
## Outcome
Created the 2026-09-14 present-tense sequencing authority and reconciled the
project brief, agent guidance, README, local release record, and application audit
with the verified 0.0.3-alpha baseline. No source code or installed release was
modified.
## Files changed
- `AGENTS.md`: points current sequencing at the 2026-09-14 authority and states
the native-readback evidence boundary.
- `PROJECT_BRIEF.md`: records multi-body as implemented and keeps assemblies,
joints, drawings, CAM, simulation, sheet metal, rendering/materials, and cloud
excluded.
- `README.md`: documents the two-binary installer contract and staged
installed-worker plus native-readback activation evidence.
- `docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md`: new live roadmap
with exactly the required top-level sections.
- `docs/LOCAL_LINUX_RELEASE_2026-09-10.md`: records 0.0.3-alpha/c111f856 as
current, 8dfd8a4 as previous, exact hashes, and the verification boundary.
- `docs/APP_ISSUES_AUDIT_2026-09-13.md`: replaces repeated stale full-gate status
with both measured runs while leaving generated warnings and portability open.
- `docs/superpowers/plans/2026-09-14-phased-next-steps.md`: marks Task 2 steps
complete.
- `.superpowers/sdd/progress.md`: marks Task 2 done with scoped evidence and
independent review still pending.
- `.superpowers/sdd/task-02-roadmap.md`: included unchanged as the Task 2
implementation contract.
- `.superpowers/sdd/task-02-report.md`: this report.
## Wording ambiguities resolved
- Kept the user's 17m17s warm full-gate measurement separate from the later
isolated-worktree 18m59.501s measurement; only the latter is tied to
`target/gate/timings-77YdHFVC.tsv` and its 1139.501-second sum.
- Defined L3 as exact spline sweep, spline-section ruled loft, edge fillet,
direct edit, and upstream dimension change. The older guided-rail/delete-face
composition is superseded; delete-face remains an existing separate capability.
- Described framebuffer readback only as proof of installed native launch/render.
Interaction, save/reopen, and export evidence belongs to the driven harness and
installed worker; no native Wayland input injection is claimed.
- Used `current` and `previous` for the 2026-09-14 managed targets while keeping
the older release's then-current rollback link inside a clearly historical
subsection.
- Treated multi-body as implemented and kept assemblies and the other named
exclusions outside the roadmap.
## Exact verification output
Command:
```sh
rg -n "2026-09-14|0.0.3-alpha|17m17|18m59|previous|driven|readback|spline|AppImage" AGENTS.md PROJECT_BRIEF.md README.md docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md docs/LOCAL_LINUX_RELEASE_2026-09-10.md docs/APP_ISSUES_AUDIT_2026-09-13.md
```
Exit: 0
```text
docs/APP_ISSUES_AUDIT_2026-09-13.md:3:Date: 2026-09-13; implementation and revalidation updated 2026-09-14
docs/APP_ISSUES_AUDIT_2026-09-13.md:5:Scope: application architecture, document and kernel integration, shell behavior, driven GUI workflows, persistence, and test/release evidence.
docs/APP_ISSUES_AUDIT_2026-09-13.md:26: baseline: the user's earlier warm run took 17m17s, and the later isolated
docs/APP_ISSUES_AUDIT_2026-09-13.md:28: `1139.501` summed step seconds (18m59.501s) in
docs/APP_ISSUES_AUDIT_2026-09-13.md:55:- Revalidation required: an older GUI finding whose lower layer or surrounding UI has changed and which should not be treated as proven current without a fresh driven reproduction.
docs/APP_ISSUES_AUDIT_2026-09-13.md:62:- `docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md` for the current
docs/APP_ISSUES_AUDIT_2026-09-13.md:65:- `docs/GUI_AUDIT_2026-09-10.md` for the original driven audit and finding identifiers.
docs/APP_ISSUES_AUDIT_2026-09-13.md:67:- Current source in `vernier-app`, `vernier-ui`, `vernier-doc`, `vernier-kernel`, and the driven harness.
docs/APP_ISSUES_AUDIT_2026-09-13.md:104:- The driven harness asserts exported geometry, selection, timeline state, document paths, frame digests, and negative controls. This is a better acceptance instrument than visual inspection alone on the current Wayland machine.
docs/APP_ISSUES_AUDIT_2026-09-13.md:130:| Exhaustive `scripts/check.fish` | Pass at `c111f856929f6c6370cb4ff5cd094bdb39f699f2` | Isolated-worktree exit 0; `target/gate/timings-77YdHFVC.tsv` sums to 1139.501 seconds (18m59.501s). The user's earlier warm run was 17m17s. These are separate measurements. |
docs/APP_ISSUES_AUDIT_2026-09-13.md:132:The 45 self-tests cover a broad and valuable surface, but they do not replace the full Rust workspace test suite or the driven GUI scripts. A self-test pass proves that the selected CLI checks passed; it does not prove that ordinary shell routing, responsive layouts, worker timing, or the entire release gate is green.
docs/APP_ISSUES_AUDIT_2026-09-13.md:209:- Add a driven positive case for touching union and a negative case for disjoint profiles, checking both body count and user-facing warning text.
docs/APP_ISSUES_AUDIT_2026-09-13.md:238:- Add a driven test that starts a slow compile, presses Undo through every surface, and asserts exactly one consistent outcome.
docs/APP_ISSUES_AUDIT_2026-09-13.md:258:- Give every driven process an isolated fixture directory, or make the shared fixture lock explicit and robust.
docs/APP_ISSUES_AUDIT_2026-09-13.md:260:- Repeat the previously flaky tests at least 20 times under both default and serialized parallelism before treating the gate as stable.
docs/APP_ISSUES_AUDIT_2026-09-13.md:276:After a region-driven extrusion uses the sketch, the timeline or value card can still be anchored to that feature. Retaining selection is confirmed by the cited cleanup path, but a consumed profile is not necessarily a deleted or uneditable sketch. The earlier wording overstated this consequence. A driven next-command reproduction is required to establish harmful routing rather than merely an unexpected selection policy.
docs/APP_ISSUES_AUDIT_2026-09-13.md:282:- Add a driven test for region select, extrude, timeline selection, value-card visibility, and the next command.
docs/APP_ISSUES_AUDIT_2026-09-13.md:291:The problem is not merely cosmetic. Defaults, implicit target bodies, and profile interpretation are important in a CAD system. Without a success readout, the user cannot easily distinguish a committed feature from a preview, a no-op, or a failed operation that left the previous body visible.
docs/APP_ISSUES_AUDIT_2026-09-13.md:297:- Move the driven assertion only after the readout is produced, then test extrude, revolve, sweep, loft, emboss, and body tools independently.
docs/APP_ISSUES_AUDIT_2026-09-13.md:317:- Add a driven test for valid repoint, invalid repoint, Escape cancellation, and a second click after cancellation.
docs/APP_ISSUES_AUDIT_2026-09-13.md:394:Status: **Revalidated green on 2026-09-14.**
docs/APP_ISSUES_AUDIT_2026-09-13.md:413:The current architecture has explicit preview state for push/pull and body tools (`crates/vernier-app/src/app.rs:79-98` and `crates/vernier-ui/src/shell/body_tools.rs`), which is a good start. The older driven audit nevertheless found stale previews after a number change, preview geometry painted as if it were committed, and uneven preview coverage. The recent fix batches do not record closure of the full preview set.
docs/APP_ISSUES_AUDIT_2026-09-13.md:426:- Add positive and negative driven checks for number change, Escape, refusal, commit, and rapid repeated edits for every interactive builder.
docs/APP_ISSUES_AUDIT_2026-09-13.md:482:`Cargo.toml` configures the renderer for Vulkan only, and `README.md` requires OCCT, a C++ toolchain, and a Vulkan driver. This is consistent with the current Linux-native scope and the driven harness's RADV requirement. It does mean that machines without a working Vulkan ICD cannot run the normal shell or the GPU-backed acceptance path. The lavapipe hot-pixel budget remains unmeasured.
docs/APP_ISSUES_AUDIT_2026-09-13.md:490:### P3-04: Visual acceptance remains dependent on the driven harness
docs/APP_ISSUES_AUDIT_2026-09-13.md:492:External screen capture is not reliable on this Wayland session, so visual claims based on "looking at the window" are not strong evidence. The driven harness's frame digests, ID-buffer picking, document assertions, exported geometry, and PNG hot-pixel budgets are the honest path. The unmeasured RADV-to-lavapipe comparison and the limited visual coverage of some previews are residual validation risks, not evidence that the renderer is currently wrong.
docs/APP_ISSUES_AUDIT_2026-09-13.md:535:Make disjoint Join impossible to select accidentally. Add a visible multiple-solid warning and a driven negative control.
docs/APP_ISSUES_AUDIT_2026-09-13.md:574:VernierCAD's core is credible for an ambitious alpha: the geometry, document, naming, and driven-test foundations are stronger than the current open issue count might suggest. The highest return now comes from tightening contracts between the worker and shell: one identity model for files, one unit parser, one busy barrier, one post-commit selection reconciliation path, and one structured result model for success and refusal.
docs/APP_ISSUES_AUDIT_2026-09-13.md:582:gate, unit suites, and focused driven evidence are green. A later isolated
docs/APP_ISSUES_AUDIT_2026-09-13.md:583:release-worktree full gate also exited 0 in 18m59.501s, alongside the user's
docs/APP_ISSUES_AUDIT_2026-09-13.md:584:earlier 17m17s warm full-gate measurement.
docs/APP_ISSUES_AUDIT_2026-09-13.md:608:| Focused driven closure | Exit 0 on RADV for disjoint Join intent, all Repoint surfaces, 1000 px ribbon reachability, 1000 px card bounds, builder success readouts, variables, fillet refusal, STEP import, cutaway, push/pull edit/refusal, and planar lettering. |
docs/APP_ISSUES_AUDIT_2026-09-13.md:609:| Exhaustive release gate | Later rerun at `c111f856929f6c6370cb4ff5cd094bdb39f699f2`: exit 0, with 1139.501 summed step seconds (18m59.501s) in `target/gate/timings-77YdHFVC.tsv`. The user's earlier warm run measured 17m17s. Dedicated ignored process/autosave tests and long driven workflows remain release-gate scope and passed in the isolated run. |
docs/APP_ISSUES_AUDIT_2026-09-13.md:627: needs a driven reproduction with a delayed worker and an explicit history oracle.
docs/APP_ISSUES_AUDIT_2026-09-13.md:649:full disk can leave invalid JSON in place of previously valid preferences.
docs/APP_ISSUES_AUDIT_2026-09-13.md:761:draft `-`, but reads `21.23456789`, the previously submitted value. This assertion
docs/APP_ISSUES_AUDIT_2026-09-13.md:767:ordering assertions. The test derives a field click from a previously captured
docs/APP_ISSUES_AUDIT_2026-09-13.md:829:`SettingsScreen::default` previously read `~/.config/vernier/ui.json`. Any unit
docs/APP_ISSUES_AUDIT_2026-09-13.md:838:in-crate app tests pin defaults and driven integration runs use their redirected
docs/APP_ISSUES_AUDIT_2026-09-13.md:924:4. Expand driven coverage for preview churn, imported-topology feature use, and
docs/LOCAL_LINUX_RELEASE_2026-09-10.md:1:# Local Linux release record — updated 2026-09-14
docs/LOCAL_LINUX_RELEASE_2026-09-10.md:3:Current: `0.0.3-alpha` at
docs/LOCAL_LINUX_RELEASE_2026-09-10.md:13:seconds (18m59.501s). The user's earlier warm full-gate measurement was 17m17s;
docs/LOCAL_LINUX_RELEASE_2026-09-10.md:20:readback each exited 0 with non-blank 1280×720 `Bgra8UnormSrgb` content of
docs/LOCAL_LINUX_RELEASE_2026-09-10.md:23:The evidence boundary matters: native readback proves installed native
docs/LOCAL_LINUX_RELEASE_2026-09-10.md:24:launch/render. The driven harness links the real app core and uses the installed
docs/LOCAL_LINUX_RELEASE_2026-09-10.md:28:`previous` retains the 2026-09-10 release for rollback:
docs/LOCAL_LINUX_RELEASE_2026-09-10.md:31:OCCT and is not yet the portable AppImage.
docs/LOCAL_LINUX_RELEASE_2026-09-10.md:45:`previous` is the 2026-09-09 alpha `1beb15dbc72ede35d5463e67d90f88f2fed2a061`.
docs/LOCAL_LINUX_RELEASE_2026-09-10.md:66:- Installed launcher readback: see the log line recorded in the session that
docs/LOCAL_LINUX_RELEASE_2026-09-10.md:69:Rollback: stop VernierCAD, then replace `current` with `previous` as
README.md:20:the prior target as `previous`, and writes a desktop entry. It checks executable
README.md:26:`file-commands-actual` and `local-linux-release` driven workflows, including
README.md:28:`VERNIER_READBACK`. The driven harness supplies interaction evidence through the
README.md:29:real app core and staged worker; native readback proves native launch/render only,
README.md:57:To roll back, stop VernierCAD, inspect `~/.local/share/vernier/previous`, and
README.md:62:if [ -L "$root/previous" ]; then
README.md:63: ln -sfn "$(readlink "$root/previous")" "$root/current.rollback" &&
README.md:66: echo "No previous local release is installed."
README.md:83:driven-GUI gate in `scripts/check.fish` runs all registered workflows after it.
PROJECT_BRIEF.md:33:`docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md`.
PROJECT_BRIEF.md:50:| **L3** | Rod-holder bracket, organic transition | loft, sweep, splines, tangency |
PROJECT_BRIEF.md:183:| **6** | Loft, sweep, splines, STEP I/O, Rhai scripting | L | L3 |
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:1:# VernierCAD daily reliability and spline-modeling plan — 2026-09-14
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:11:daily-use state and persistence gaps, make exact spline sweeps usable, add exact
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:12:spline-section ruled lofts and pass the revised L3 part, then strengthen measured
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:19:- The managed local release is `0.0.3-alpha` from
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:23: `8dfd8a4309c7bfd5afbe4794ce21bb2c2d6e650b` remains the `previous` rollback
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:29:- L0, L1, L2, and the existing multi-body workflow have driven and headless
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:31:- The 2026-09-14 isolated-worktree full gate at the release revision exited 0.
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:33: sum to `1139.501` seconds (18m59.501s). This is a separate measurement from the
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:34: user's earlier warm full-gate run of 17m17s; neither replaces the other.
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:51:- Exact open spline paths, exact closed cubic-spline profiles, and exact spline
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:57: alpha, not the portable AppImage promised by the release design.
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:73:cancel, include a realistic positive control, and finish with driven geometry and
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:78:Make exact spline sweeps usable through the real shell:
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:80:1. Admit exact open planar fit-point and cubic spline paths through document and
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:83: scale, upstream spline editing, save/reopen, and STEP/STL export.
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:84:3. Admit exact closed cubic-spline profiles while keeping fit-point splines
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:88:hang, and a single spline edge can contain internal C0 corners. Phase 3 does not
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:90:spline drawing.
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:94:Add exact spline-section ruled lofts with deterministic section orientation and
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:95:OCCT-history-derived provenance, then complete the 2026-09-14 L3 rod-holder gate.
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:97:The L3 acceptance is exactly: an exact spline sweep, a spline-section ruled loft,
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:104:The Phase 3/4 boundary is deliberate: Phase 4 adds ruled lofts from exact spline
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:106:3D spline drawing.
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:119: reproducible VernierCAD AppImage containing both app and worker.
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:128:spline drawing, and spline-plus-guide sweeps are also excluded from Phases 3 and
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:139:Staged and managed native readback each exited 0 and produced non-blank
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:140:1280×720 `Bgra8UnormSrgb` content of 2,764,816 bytes. Native readback proves that
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:146:interaction, save/reopen, and export checks. This is driven real-shell evidence,
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:155:- `previous`:
AGENTS.md:12:**Current sequencing is the plan of 2026-09-14, not the phase table below.**
AGENTS.md:13:`docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md` supersedes the 2026-09-06 plan and
AGENTS.md:18:milestone is the driven-GUI harness** (`vernier-drive`, milestone M1): agents drive the real
AGENTS.md:22:evidence and a script is the only honest substitute. Native framebuffer readback proves
AGENTS.md:31:**M0 (foundation) landed 2026-09-06; M1 (the driven-GUI harness) landed the same day except
AGENTS.md:39:sidecar atomically (temp + rename beside the destination, the previous sidecar kept as `.bak`
AGENTS.md:57:through `vernier_ui::testing::driven`** (bounded, panic and hang distinguished), the app crate's
AGENTS.md:163:**Five gaps the driven gate found, none of them fixed here.** (1) **A circular bore cannot be
AGENTS.md:592:through a fillet without exploding. ~~Deferred from the Phase 4 row (FUSION_LOG-driven
AGENTS.md:617:face (proven headlessly via the ID-buffer readback test). Deliberately deferred from Phase 1:
```
Command:
```sh
git diff --check
```
Exit: 0
```text
(no output)
```
## Self-review
- Completeness: all six required product documents are covered; the new roadmap
has exactly Direction, Completed baseline, Verified gaps, Phase 2, Phase 3,
Phase 4, Phase 5, Deliberate exclusions, and Acceptance evidence as top-level
sections.
- Factual precision: exact revision, release version, hashes, timing file,
durations, targets, drive modes, readback dimensions/format/size, and open gaps
match the task brief.
- Naming: user-facing prose says VernierCAD; binary, crate, script, and managed
path identifiers use `vernier` spellings.
- Evidence boundary: no framebuffer claim is used as pointer/keyboard interaction
evidence; driven and native readback claims remain distinct.
- Scope: documentation and task-ledger files only; no source or installed-release
mutation.
## Concerns
None. Independent review remains pending as recorded in the ledger.
+58
View File
@@ -0,0 +1,58 @@
# Task 2 brief — Refresh the live roadmap and release record
Base SHA: `c111f856929f6c6370cb4ff5cd094bdb39f699f2`
Worktree: `/home/nilsb/Documents/Projects/VernierCAD/target/worktrees/phased-next-steps`
## Deliverable
Create `docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md` as the present-tense sequencing authority and reconcile `AGENTS.md`, `PROJECT_BRIEF.md`, `README.md`, `docs/LOCAL_LINUX_RELEASE_2026-09-10.md`, and `docs/APP_ISSUES_AUDIT_2026-09-13.md` with it.
The new authority must have exactly these top-level sections after its title: `Direction`, `Completed baseline`, `Verified gaps`, `Phase 2`, `Phase 3`, `Phase 4`, `Phase 5`, `Deliberate exclusions`, and `Acceptance evidence`. It must distinguish completed features, verified gaps, and deliberate exclusions rather than mixing them into a stale phase narrative.
## Verified release evidence to record
- Full isolated-worktree gate at `c111f856929f6c6370cb4ff5cd094bdb39f699f2`: exit 0; timing file `target/gate/timings-77YdHFVC.tsv`; summed step duration `1139.501` seconds (18m59.501s). The user's earlier measured warm run was 17m17s; label these two measurements accurately rather than replacing one with the other.
- Release build: `cargo build --locked --release -p vernier-app --bin vernier-app --bin vernier-worker`, exit 0.
- Staged release version `0.0.3-alpha`; app SHA-256 `2c0c186e9a74cf9ce52a3433ae1f30c19a8d386d24c61e366222aef3f0d2519b`; worker SHA-256 `a61054391048090b17e1e34b8467f5eb705acacf112c08ed5840119b992a2d9b`.
- Staged installed-worker drives `phase1-file-commands-installed` and `phase1-local-release-installed`: `result=pass`, `determinism_mode=cross-process`, `deterministic=true`. They produced and reopened documents and exported STEP/STL; matching artifacts compare byte-for-byte across the two processes.
- Staged and managed native readback: exit 0, 1280x720 Bgra8UnormSrgb, 2,764,816 bytes, non-blank content verdict.
- Managed current target: `/home/nilsb/.local/share/vernier/releases/c111f856929f6c6370cb4ff5cd094bdb39f699f2`.
- Managed previous target retained for rollback: `/home/nilsb/.local/share/vernier/releases/8dfd8a4309c7bfd5afbe4794ce21bb2c2d6e650b`.
- Verification boundary: native readback proves installed native launch/render. The driven harness links the real app core and uses the installed worker for interaction, save/reopen, and export evidence; it does not inject input into the native Wayland window.
- The generated `cxxbridge` `-Wmaybe-uninitialized` warnings remain open for sanitizer-backed investigation. The current release dynamically resolves system OCCT and is not yet the portable AppImage.
## Required documentation changes
1. `AGENTS.md`: point current sequencing at the 2026-09-14 authority while retaining the older phase record as history.
2. `PROJECT_BRIEF.md`: correct the old multi-body exclusion; multi-body is implemented, while assemblies, joints, drawings, CAM, simulation, sheet metal, rendering/materials, and cloud remain excluded.
3. `README.md`: state that a local installed release requires both `vernier-app` and `vernier-worker`, the installer accepts `RELEASE_DIR VERSION HEX_COMMIT`, and activation follows the staged installed-worker drives plus native readback.
4. `docs/LOCAL_LINUX_RELEASE_2026-09-10.md`: append or replace status so `0.0.3-alpha`/`c111f856...` is current and `8dfd8a4...` is previous, with the exact verification boundary and hashes.
5. `docs/APP_ISSUES_AUDIT_2026-09-13.md`: replace the stale “full gate has not been rerun” status with both measured results; do not close the generated-warning or portable-release gaps.
6. Define the 2026-09-14 L3 acceptance explicitly as exact spline sweep + spline-section ruled loft + edge fillet + direct edit + upstream dimension change. State that this supersedes the older guided-rail/delete-face L3 composition; spline-plus-guide remains refused because the measured auxiliary-spine route can hang, while delete-face remains a separate existing capability.
## Global constraints
- Product copy says `VernierCAD`; code and crate names say `vernier`.
- No feature implementation in this task.
- Do not modify or remove any managed installed release.
- Do not claim native pointer/keyboard interaction from a framebuffer readback.
- Keep the Phase 3/4 boundary explicit: no smooth guided lofts, G1/G2 continuity, periodic lofts, or arbitrary 3D spline drawing.
- Keep assemblies, drawings, CAM, simulation, and cloud excluded.
## Verification and commit
Run:
```bash
rg -n "2026-09-14|0.0.3-alpha|17m17|18m59|previous|driven|readback|spline|AppImage" AGENTS.md PROJECT_BRIEF.md README.md docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md docs/LOCAL_LINUX_RELEASE_2026-09-10.md docs/APP_ISSUES_AUDIT_2026-09-13.md
git diff --check
```
Commit only these documentation files plus `docs/superpowers/plans/2026-09-14-phased-next-steps.md`, `.superpowers/sdd/progress.md`, and this task brief:
```bash
git add AGENTS.md PROJECT_BRIEF.md README.md docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md docs/LOCAL_LINUX_RELEASE_2026-09-10.md docs/APP_ISSUES_AUDIT_2026-09-13.md docs/superpowers/plans/2026-09-14-phased-next-steps.md .superpowers/sdd/progress.md .superpowers/sdd/task-02-roadmap.md
git commit -m "docs: set daily reliability and spline roadmap"
```
Write the implementation report to `.superpowers/sdd/task-02-report.md` before committing and include it in the same commit. The report must list changed files, exact verification output, and any wording ambiguity resolved.
+8 -7
View File
@@ -9,17 +9,18 @@ in code.
Read `PROJECT_BRIEF.md` before your first substantial change. Read `MISTAKES.md` before
debugging anything that smells familiar.
**Sequencing is the plan of 2026-09-06, not the phase table below.**
`docs/PLAN_2026-09-06_shapr3d-competitor.md` supersedes the phases for *what to build next*;
the phase record that follows stands as the account of what was built and why. Three things
it changes. **Multi-body is IN scope, as milestone M5** — the brief's "Phase 8+, if ever"
line is amended for multi-body only; assemblies, joints, 2D drawings and rendering/materials
stay out, as do CAM, simulation, sheet metal and cloud. **The acceptance instrument for every
**Current sequencing is the plan of 2026-09-14, not the phase table below.**
`docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md` supersedes the 2026-09-06 plan and
the phases below for *what to build next*. The older plan and the phase record that follows
remain the account of what was built and why. **Multi-body is implemented and in scope**;
assemblies, joints, 2D drawings and rendering/materials stay out, as do CAM, simulation,
sheet metal and cloud. **The acceptance instrument for every
milestone is the driven-GUI harness** (`vernier-drive`, milestone M1): agents drive the real
shell headlessly through scripted egui input and assert the *exported geometry*, because
nothing on this machine can capture a real window — external screen capture returns
transparent frames on this Wayland session, so "we looked at it" is not available as
evidence and a script is the only honest substitute. **Four new crates are approved** and
evidence and a script is the only honest substitute. Native framebuffer readback proves
installed launch/render, not pointer or keyboard interaction in the Wayland window. **Four new crates are approved** and
need no further ask: `rfd` (native file dialogs), `directories` (XDG paths), `image` (PNG
only, harness goldens) and `rayon` (parallel tessellation from M5). Anything else is still a
separate discussion. Finally, `docs/GAP_MAP_2026-09-06.md` and its matrix/findings companions
+8 -5
View File
@@ -24,10 +24,13 @@ A single-user, solid-modelling CAD app:
- Exports STL and STEP; imports STEP
- Runs headless from a CLI for scripting and testing
**Not** in scope, ever: CAM, simulation, generative design, cloud sync, rendering/animation,
sheet metal, surfacing beyond loft/sweep. Fusion has those. This does not.
**Not** in scope: assemblies, joints, 2D drawings, CAM, simulation, generative design,
cloud sync, rendering/materials, sheet metal, and surfacing beyond loft/sweep. Fusion has
those. This does not.
**Not** in scope, for now: assemblies, joints, 2D drawings, multi-body. Phase 8+, if ever.
**Multi-body is implemented.** The old Phase 8+ exclusion no longer applies to multi-body;
the other exclusions above remain deliberate. Current sequencing lives in
`docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md`.
## 2. Success metric
@@ -45,7 +48,7 @@ out of the machine sooner.
| **L1** | Bike light mount | constraints, holes, fillets, **edit-after-the-fact** |
| **L2** | Snapmaker jig / enclosure | shell, draft, patterns, naming under stress |
| **L3** | Rod-holder bracket, organic transition | loft, sweep, splines, tangency |
| **L4** | Two-part snap-fit case | multi-body, joints, assembly context |
| **L4** | Two-part snap-fit case | multi-body modeling and explicit body context |
**L1 is the real milestone.** Anything can do L0. L1 requires that a sketch edit propagates
through a fillet without exploding — which is the entire problem this project exists to solve.
@@ -179,7 +182,7 @@ is a coffee break.
| **5** | UX: gizmos, contextual toolbar, snapping, measure, sections, direct push/pull | M | L2 |
| **6** | Loft, sweep, splines, STEP I/O, Rhai scripting | L | L3 |
| **7** | Perf: parallel branch recompute, tess cache, LOD, kernel fully off render thread | M | 500-feature model recomputes with no frame drop |
| **8+** | Multi-body, joints, assemblies, drawings | XL | L4 |
| **8+** | Historical phase placeholder; see the current dated plan | XL | L4 |
Rough calibration at ~10 h/week with heavy agent use: L0 around month 4–5, L1 around month 7–9.
+13 -3
View File
@@ -5,11 +5,13 @@ kernel, and a native Wayland-capable winit window. It is GPL-3.0-or-later.
## Install a local release
Provide a directory containing executable `vernier-app` and `vernier-worker`,
then install it without modifying the source checkout:
Provide a release directory containing executable `vernier-app` and
`vernier-worker`. A local installed release requires both binaries. The installer
accepts `RELEASE_DIR VERSION HEX_COMMIT` and installs without modifying the source
checkout:
```sh
scripts/install-local.sh /path/to/release 0.0.1-alpha "$(git rev-parse HEAD)"
scripts/install-local.sh RELEASE_DIR VERSION HEX_COMMIT
~/.local/bin/vernier
```
@@ -20,6 +22,14 @@ bits and missing shared libraries with `ldd`. Existing user data and releases
owned by another commit are preserved. Use `--dry-run` to inspect destinations;
use `--prefix DIR` for a self-contained staging prefix.
Before activation, install under a staging prefix and run the installed-worker
`file-commands-actual` and `local-linux-release` driven workflows, including
save/reopen and STEP/STL export. Then run the staged native launcher with
`VERNIER_READBACK`. The driven harness supplies interaction evidence through the
real app core and staged worker; native readback proves native launch/render only,
not pointer or keyboard injection into the Wayland window. Move `current` only
after both evidence paths pass.
The launcher does not depend on the checkout or a working directory. It keeps
the current log and one rotated log under
`${XDG_STATE_HOME:-~/.local/state}/vernier/logs`. It does not force an obsolete
+16 -12
View File
@@ -22,8 +22,11 @@ The original P1 application defects in this report are now fixed. The most
important remaining risks are narrower and should not be confused with a
bug-free or production-ready claim:
1. The exhaustive release gate has not been rerun end to end after this pass;
the new fast developer gate and focused driven workflows are green.
1. Two exhaustive release-gate measurements are now recorded for the release
baseline: the user's earlier warm run took 17m17s, and the later isolated
worktree run at `c111f856929f6c6370cb4ff5cd094bdb39f699f2` exited 0 with
`1139.501` summed step seconds (18m59.501s) in
`target/gate/timings-77YdHFVC.tsv`.
2. Generated `cxx` C++ bridge code still emits `-Wmaybe-uninitialized`
diagnostics and has not been independently cleared with sanitizers.
3. Static release linkage still does not match the brief's locked OCCT
@@ -56,7 +59,8 @@ The current evidence does not support saying that every finding in the large 202
### Reviewed material
- `PROJECT_BRIEF.md`, `MISTAKES.md`, `README.md`, and `AGENTS.md`.
- `docs/PLAN_2026-09-06_shapr3d-competitor.md` for the current product sequence and scope.
- `docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md` for the current
product sequence and scope; the 2026-09-06 plan remains historical context.
- `docs/GAP_MAP_2026-09-06.md` as historical architecture context. Its header explicitly marks it as point-in-time material, so it was not used as proof of present behavior.
- `docs/GUI_AUDIT_2026-09-10.md` for the original driven audit and finding identifiers.
- `docs/FIXES_2026-09-11_audit-batch-1.md`, `docs/FIXES_2026-09-11_audit-batch-2.md`, and `docs/FIXES_2026-09-13_audit-batch-3.md` for closure status and recent reproductions.
@@ -123,7 +127,7 @@ The current open bugs are consistent with state being updated in one layer but n
| `scripts/check-fast.fish` | Pass, 2m30s warm build | Runs format, strict lint, nonduplicated Rust tests, 45 self-checks, drive library tests, and one real RADV shell/export smoke. |
| Planar lettering | Pass: 93 steps twice, cross-process deterministic | Current committed selector, matching worker, RADV, save/reload, and seven STEP artifacts. |
| Focused RADV revalidation | Pass | Variables, fillet refusal, STEP import, cutaway/section, push/pull edit/refusal, 1000 px ribbon, card bounds, success readouts, Repoint surfaces, and disjoint Join intent. |
| Exhaustive `scripts/check.fish` | Not rerun to completion | Its generic Cargo phase no longer duplicates the drive integration workflows that it runs explicitly. Release-only breadth remains distinct from the fast gate. |
| Exhaustive `scripts/check.fish` | Pass at `c111f856929f6c6370cb4ff5cd094bdb39f699f2` | Isolated-worktree exit 0; `target/gate/timings-77YdHFVC.tsv` sums to 1139.501 seconds (18m59.501s). The user's earlier warm run was 17m17s. These are separate measurements. |
The 45 self-tests cover a broad and valuable surface, but they do not replace the full Rust workspace test suite or the driven GUI scripts. A self-test pass proves that the selected CLI checks passed; it does not prove that ordinary shell routing, responsive layouts, worker timing, or the entire release gate is green.
@@ -573,11 +577,11 @@ The app should not be called bug-free or production-ready. Foreign-target
overwrite consent, disjoint Join semantics, asynchronous preference I/O, region
selection cleanup, generic Repoint routing, narrow command reachability, and
builder confirmation are now implemented and covered. Release linkage,
accessibility, sanitizer coverage, imported-topology breadth, saved-view section
round trips, and the exhaustive release gate remain open verification or product
work. The current fast gate, unit suites, and focused driven evidence are green;
the exhaustive release gate was deliberately not repeated end to end in this
pass.
accessibility, sanitizer coverage, imported-topology breadth, and saved-view
section round trips remain open verification or product work. The current fast
gate, unit suites, and focused driven evidence are green. A later isolated
release-worktree full gate also exited 0 in 18m59.501s, alongside the user's
earlier 17m17s warm full-gate measurement.
## Expanded Review: Evidence and Corrections
@@ -602,7 +606,7 @@ not fault-injected are explicitly marked.
| CLI self-test | 45 of 45 checks passed in the fast gate. |
| Fast development gate | Exit 0 in 2m30s on the final warm working tree. It runs formatting, lint, the non-duplicated workspace tests, CLI self-test, drive unit/static checks, and one real-shell RADV smoke workflow. Its first run also caught two new modules missing from the source-reachability census; registering them made the rerun green. |
| Focused driven closure | Exit 0 on RADV for disjoint Join intent, all Repoint surfaces, 1000 px ribbon reachability, 1000 px card bounds, builder success readouts, variables, fillet refusal, STEP import, cutaway, push/pull edit/refusal, and planar lettering. |
| Exhaustive release gate | Not rerun end to end after the corrections. Its acceptance scripts are no longer executed once as drive integration tests and then again explicitly. Dedicated ignored process/autosave tests and all long driven workflows remain release-gate scope. |
| Exhaustive release gate | Later rerun at `c111f856929f6c6370cb4ff5cd094bdb39f699f2`: exit 0, with 1139.501 summed step seconds (18m59.501s) in `target/gate/timings-77YdHFVC.tsv`. The user's earlier warm run measured 17m17s. Dedicated ignored process/autosave tests and long driven workflows remain release-gate scope and passed in the isolated run. |
| Release benchmark | Exit 0 for sizes 10, 50, 200. Numbers and limitations below. |
The initial red workspace and lettering runs remain useful as positive controls:
@@ -890,7 +894,7 @@ implementation or tests cover part of the contract; it never means defect-free.
| Sketch creation, constraints, drag, trim, patterns | Partial | Substantial existing tests; exhaustive gestures and constraint combinations not rerun. |
| Upstream edits and parametric history | Partial, current focused tests green | The corrected parameter reply test reaches and passes its held-response assertions; deferred GUI workflows remain. |
| Millimeters/f64, radians internally | Pass in inspected numeric editor paths | Body-tool arithmetic now uses the active display-unit scale and persists explicit unit meaning; focused inch, centimeter, bare, explicit-unit, and ambiguous-mixed-expression tests pass. |
| Deterministic documents and geometry | Partial | Established naming/selftest machinery; full current cross-process gate not certified. |
| Deterministic documents and geometry | Strong, still bounded by registered workflows | Established naming/selftest machinery; the current isolated full gate passed, and the staged installed-worker drives passed cross-process with byte-identical matching artifacts. |
| No unsafe outside OCCT bridge | Pass for inspected crate roots | `forbid(unsafe_code)` present in primary Rust libraries and app/CLI roots. Not a C++ memory-safety proof. |
| Document/solver independent of kernel | Pass for manifests | Neither manifest imports the kernel; doc depends on solver. |
| History-derived face/edge identity | Partial | Persisted reference machinery exists; no exhaustive operation-delta audit in this pass. |
@@ -900,7 +904,7 @@ implementation or tests cover part of the contract; it never means defect-free.
| Preferences survive failure | Strong, partial | Replacement is atomic and failure-tested; writes are coalesced off the UI thread and latest-generation acknowledgement is pinned. Crash timing beyond the injected boundary is not exhaustively modeled. |
| STEP input validation | Bounded bytes | 64 MiB streaming cap and regular-file check; adversarial parser corpus and expanded topology budgets not tested. |
| STL/STEP output and multi-body selection | Partial | Existing tests, no exhaustive export matrix in this pass. |
| Text, emboss, engraving | Workflow and gate pass | All 93 committed-script steps passed twice on RADV with cross-process deterministic artifacts; exhaustive release gate was not rerun in full. |
| Text, emboss, engraving | Workflow and gate pass | All 93 committed-script steps passed twice on RADV with cross-process deterministic artifacts; the later isolated full release gate also exited 0. |
| Recovery, stop, restart, autosave | Partial | Host non-ignored worker tests passed; dedicated ignored acceptance not rerun. |
| Accessibility and keyboard-only modelling | Unverified | No assistive-technology or full focus-navigation audit. No WCAG compliance claim. |
| Small windows, scaling, long names | Partial, focused paths green | Essential solid commands and card bounds pass at 1000 by 640; exhaustive viewport, scale-factor, and long-content grids were not rerun. |
+33 -1
View File
@@ -1,4 +1,36 @@
# Local Linux release — 2026-09-10
# Local Linux release record — updated 2026-09-14
Current: `0.0.3-alpha` at
`c111f856929f6c6370cb4ff5cd094bdb39f699f2`.
Release: `~/.local/share/vernier/releases/c111f856929f6c6370cb4ff5cd094bdb39f699f2`
- vernier-app `2c0c186e9a74cf9ce52a3433ae1f30c19a8d386d24c61e366222aef3f0d2519b`
- vernier-worker `a61054391048090b17e1e34b8467f5eb705acacf112c08ed5840119b992a2d9b`
The locked two-binary release build exited 0. The isolated-worktree full gate at
this revision exited 0; `target/gate/timings-77YdHFVC.tsv` sums to `1139.501`
seconds (18m59.501s). The user's earlier warm full-gate measurement was 17m17s;
it is a separate measurement, not replaced by the isolated run.
The staged installed-worker drives `phase1-file-commands-installed` and
`phase1-local-release-installed` both passed with cross-process determinism. They
produced and reopened documents, exported STEP/STL, and produced matching
byte-for-byte artifacts across the two processes. Staged and managed native
readback each exited 0 with non-blank 1280×720 `Bgra8UnormSrgb` content of
2,764,816 bytes.
The evidence boundary matters: native readback proves installed native
launch/render. The driven harness links the real app core and uses the installed
worker for interaction, save/reopen, and export evidence; it does not inject
pointer or keyboard input into the native Wayland window.
`previous` retains the 2026-09-10 release for rollback:
`~/.local/share/vernier/releases/8dfd8a4309c7bfd5afbe4794ce21bb2c2d6e650b`.
Neither release was removed. The current alpha still dynamically resolves system
OCCT and is not yet the portable AppImage.
## Previous release record — 2026-09-10
Installed: `8dfd8a4309c7bfd5afbe4794ce21bb2c2d6e650b`, the merge of main into the
reliability branch (`docs/MERGE_2026-09-10_main-into-reliability.md`), as
@@ -0,0 +1,156 @@
# VernierCAD daily reliability and spline-modeling plan — 2026-09-14
## Direction
This document is the present-tense sequencing authority for what VernierCAD
builds next. The 2026-09-06 Shapr3D-competitor plan and the phase record in
`AGENTS.md` remain historical accounts of what was built and why; they no longer
set the order of new work.
The next release sequence is: preserve the installed stable baseline, close the
daily-use state and persistence gaps, make exact spline sweeps usable, add exact
spline-section ruled lofts and pass the revised L3 part, then strengthen measured
performance and portable Linux release evidence. No phase advances on source
inspection or a green unit test alone: the real shell, fresh-process persistence,
and exported geometry are the acceptance path.
## Completed baseline
- The managed local release is `0.0.3-alpha` from
`c111f856929f6c6370cb4ff5cd094bdb39f699f2`. Both `vernier-app` and
`vernier-worker` were built together with the locked release build and installed
together. The September 10 release at
`8dfd8a4309c7bfd5afbe4794ce21bb2c2d6e650b` remains the `previous` rollback
target.
- The current alpha includes the history-based document and naming store,
constrained sketching, save/reopen and recovery, STL/STEP I/O, direct edits,
loft and sweep for their currently admitted inputs, and multi-body modeling
with explicit body targets and booleans.
- L0, L1, L2, and the existing multi-body workflow have driven and headless
coverage. Multi-body is implemented; it is not a future Phase 8 exclusion.
- The 2026-09-14 isolated-worktree full gate at the release revision exited 0.
Its timing record is `target/gate/timings-77YdHFVC.tsv`, whose step durations
sum to `1139.501` seconds (18m59.501s). This is a separate measurement from the
user's earlier warm full-gate run of 17m17s; neither replaces the other.
- The locked release build
`cargo build --locked --release -p vernier-app --bin vernier-app --bin vernier-worker`
exited 0. The staged binaries' SHA-256 values are
`2c0c186e9a74cf9ce52a3433ae1f30c19a8d386d24c61e366222aef3f0d2519b`
for `vernier-app` and
`a61054391048090b17e1e34b8467f5eb705acacf112c08ed5840119b992a2d9b`
for `vernier-worker`.
## Verified gaps
- Disposable previews do not yet have one generation identity and invalidation
rule across every feature, body tool, sketch-point, and push/pull path.
- Imported-body capabilities do not yet reach every readiness surface, so some
unsupported parametric edits can be refused only after dispatch.
- Saved camera and section state do not yet have a paired fresh-process,
validate-then-commit restoration gate.
- Exact open spline paths, exact closed cubic-spline profiles, and exact spline
sections are not yet admitted across the complete document, OCCT, shell,
persistence, and export path.
- Generated `cxxbridge` `-Wmaybe-uninitialized` warnings remain open. They are
not evidence of a runtime defect and require sanitizer-backed investigation.
- The current release dynamically resolves system OCCT. It is a managed local
alpha, not the portable AppImage promised by the release design.
- Representative L2–L4 latency, memory, CI parity, and software-renderer golden
tolerance still need the measurements specified below.
## Phase 2
Close daily-use reliability gaps before extending modeling scope:
1. Give every disposable preview a monotonically increasing generation and one
discard path, including failure, cancel, apply, history, and file transitions.
2. Propagate imported-body capabilities into selection and readiness so the
shell explains unsupported face, edge, and boolean-target edits before dispatch.
3. Make saved-view and saved-section restoration atomic, migration-backed, and
proven across separate writer and reader processes.
Each change must preserve authored document and naming state on refusal or
cancel, include a realistic positive control, and finish with driven geometry and
fresh-process persistence evidence.
## Phase 3
Make exact spline sweeps usable through the real shell:
1. Admit exact open planar fit-point and cubic spline paths through document and
OCCT validation, without replacing curves with polylines.
2. Wire candidate preview, cancel, apply, target-body selection, orientation,
scale, upstream spline editing, save/reopen, and STEP/STL export.
3. Admit exact closed cubic-spline profiles while keeping fit-point splines
open-only unless their document representation gains an explicit closed form.
Spline-plus-guide remains a named refusal: the measured auxiliary-spine route can
hang, and a single spline edge can contain internal C0 corners. Phase 3 does not
promise smooth guided lofts, G1/G2 continuity, periodic lofts, or arbitrary 3D
spline drawing.
## Phase 4
Add exact spline-section ruled lofts with deterministic section orientation and
OCCT-history-derived provenance, then complete the 2026-09-14 L3 rod-holder gate.
The L3 acceptance is exactly: an exact spline sweep, a spline-section ruled loft,
an edge fillet, a direct edit, and an upstream dimension change, built through the
real shell and checked after save/reopen and STEP/STL export. This supersedes the
older guided-rail/delete-face L3 composition. Delete-face remains a separate
existing capability, not an L3 requirement. Spline-plus-guide remains refused for
the measured hang risk described in Phase 3.
The Phase 3/4 boundary is deliberate: Phase 4 adds ruled lofts from exact spline
sections, not smooth guided lofts, G1/G2 continuity, periodic lofts, or arbitrary
3D spline drawing.
## Phase 5
Strengthen performance and release confidence after L3:
1. Measure cold compile, preview, apply, tail-edit latency, and peak RSS on
representative L2, L3, and multi-body L4 fixtures in fresh processes.
2. Make local and CI acceptance consume one machine-readable gate manifest, then
calibrate software-renderer golden tolerances only from actual measurements.
3. Inventory the generated C++ warnings and run the relevant OCCT paths under
ASan/UBSan before suppressing or fixing anything.
4. Pin and build OCCT for static release linkage, then build and certify a
reproducible VernierCAD AppImage containing both app and worker.
5. Run the complete integrated gate and staged installed-worker workflows before
moving the managed launcher again; retain the rollback release.
## Deliberate exclusions
Assemblies, joints, 2D drawings, CAM, simulation, sheet metal,
rendering/materials, generative design, and cloud services are outside this
roadmap. Smooth guided lofts, G1/G2 continuity, periodic lofts, arbitrary 3D
spline drawing, and spline-plus-guide sweeps are also excluded from Phases 3 and
4. These boundaries do not remove existing delete-face or multi-body capability.
## Acceptance evidence
The staged installed-worker drives `phase1-file-commands-installed` and
`phase1-local-release-installed` both report `result=pass`,
`determinism_mode=cross-process`, and `deterministic=true`. They produced and
reopened native documents, exported STEP and STL, and compared matching artifacts
byte-for-byte across the two processes.
Staged and managed native readback each exited 0 and produced non-blank
1280×720 `Bgra8UnormSrgb` content of 2,764,816 bytes. Native readback proves that
the installed native application launches and renders. It does not prove native
pointer or keyboard interaction in the Wayland window.
Interaction evidence comes from `vernier-drive`: it links the real app core,
drives scripted egui input, and uses the installed `vernier-worker` for
interaction, save/reopen, and export checks. This is driven real-shell evidence,
but it does not inject input into the native Wayland window. Exported geometry,
document/naming assertions, cross-process determinism, and designed negative
controls remain the authoritative acceptance evidence.
The managed targets recorded for this baseline are:
- `current`:
`/home/nilsb/.local/share/vernier/releases/c111f856929f6c6370cb4ff5cd094bdb39f699f2`
- `previous`:
`/home/nilsb/.local/share/vernier/releases/8dfd8a4309c7bfd5afbe4794ce21bb2c2d6e650b`
@@ -0,0 +1,921 @@
# VernierCAD Phased Next Steps Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Deliver a verified local baseline, close daily-use state-management gaps, make exact spline sweeps and spline-section ruled lofts usable through the real shell, complete the L3 rod-holder workflow, and certify a portable Linux release.
**Architecture:** Keep the document server as the single owner of the document, naming store, and OCCT kernel. Preview requests gain explicit generations and disposable-state semantics; imported-body capabilities travel in scene metadata so the shell can refuse unsupported edits before dispatch. Spline support reuses the document's existing fit-point and cubic representations and the façade's exact `Geom_BSplineCurve`/`GeomAPI_Interpolate` construction, while sweep/loft provenance remains OCCT-history-derived. Each phase ends in driven GUI, fresh-process persistence, exported-geometry, and negative-control evidence before the installed release moves again.
**Tech Stack:** Rust 2024 workspace, `cxx` OCCT façade, egui/wgpu shell, `vernier-drive`, fish/bash release scripts, Linux AppImage tooling already available on the build host. No new Rust dependency is approved by this plan.
## Global Constraints
- Every modeling phase requires driven GUI coverage, independent exported-geometry checks, explicit target-body behavior, and persistence tests.
- Keep the installed release unchanged until its replacement passes validation.
- Preserve the current conservative document-locking policy.
- Unsupported imported-face and imported-edge operations must be disabled before dispatch and explain what the user can do instead.
- Spline sweeps and lofts must use exact kernel curves; polygonal substitution is forbidden.
- Start sweep support with open planar spline paths and existing closed profiles, then add closed spline profiles.
- Smooth guided lofts, G1/G2 continuity, periodic lofts, and arbitrary 3D spline drawing remain outside this implementation.
- Assemblies, drawings, CAM, simulation, and cloud remain outside this roadmap.
- Product copy says `VernierCAD`; code and crate names say `vernier`.
- `rfd`, `directories`, PNG-only `image`, and `rayon` are the only pre-approved dependency additions; this plan adds none.
- Linux acceptance uses the driven-GUI harness because this Wayland session cannot provide trustworthy external window capture.
---
## Phase 1 — Deliver the stable baseline
### Task 1: Validate and atomically install revision `c111f856929f6c6370cb4ff5cd094bdb39f699f2`
**Files:**
- Verify: `scripts/check.fish`
- Verify: `scripts/install-local.sh`
- Verify: `scripts/drive/file-commands-actual.json`
- Verify: `scripts/drive/local-linux-release.json`
- Create as ignored evidence: `target/release-input/c111f856/`
- Create as ignored evidence: `target/install-smoke/c111f856/`
**Interfaces:**
- Consumes: release binaries `target/release/vernier-app` and `target/release/vernier-worker` built from the immutable revision.
- Produces: managed release `0.0.3-alpha`, `~/.local/share/vernier/current`, rollback link `~/.local/share/vernier/previous`, hashes, driven reports, and native readback evidence.
- [ ] **Step 1: Create a clean detached release-validation worktree and run the full gate**
```bash
git worktree add --detach /home/nilsb/Documents/Projects/VernierCAD/target/worktrees/release-c111f856 c111f856929f6c6370cb4ff5cd094bdb39f699f2
cd /home/nilsb/Documents/Projects/VernierCAD/target/worktrees/release-c111f856
test "$(git rev-parse HEAD)" = c111f856929f6c6370cb4ff5cd094bdb39f699f2
test -z "$(git status --porcelain)"
fish scripts/check.fish
```
Expected: exit 0; every positive drive exits 0 and the prescribed negative controls exit 1 at their own expectation steps.
- [ ] **Step 2: Build both installed binaries and stage them together**
```bash
cargo build --locked --release -p vernier-app --bin vernier-app --bin vernier-worker
mkdir -p target/release-input/c111f856
install -m 0755 target/release/vernier-app target/release-input/c111f856/vernier-app
install -m 0755 target/release/vernier-worker target/release-input/c111f856/vernier-worker
sha256sum target/release-input/c111f856/vernier-app target/release-input/c111f856/vernier-worker
```
Expected: both binaries exist, are executable, and have recorded non-empty SHA-256 values.
- [ ] **Step 3: Exercise the installer under an isolated prefix**
```bash
scripts/install-local.sh --dry-run --prefix "$PWD/target/install-smoke/c111f856" "$PWD/target/release-input/c111f856" 0.0.3-alpha c111f856929f6c6370cb4ff5cd094bdb39f699f2
scripts/install-local.sh --prefix "$PWD/target/install-smoke/c111f856" "$PWD/target/release-input/c111f856" 0.0.3-alpha c111f856929f6c6370cb4ff5cd094bdb39f699f2
```
Expected: dry-run makes no prefix changes; install creates an immutable release, a launcher, a manifest, and a `current` link.
- [ ] **Step 4: Run installed-worker file and release workflows**
```bash
target/release/vernier-drive scripts/drive/file-commands-actual.json --out target/drive/phase1-file-commands-installed --require-adapter RADV --worker-helper "$PWD/target/install-smoke/c111f856/share/vernier/current/vernier-worker"
target/release/vernier-drive scripts/drive/local-linux-release.json --out target/drive/phase1-local-release-installed --require-adapter RADV --worker-helper "$PWD/target/install-smoke/c111f856/share/vernier/current/vernier-worker"
```
Expected: exit 0; the reports prove launch-linked editing, save/reopen, STEP export, and STL export through the staged installed worker. The report must state that native readback proves native launch/render while the driven harness supplies interaction evidence.
- [ ] **Step 5: Smoke the staged native launcher**
```bash
timeout 30s env XDG_CONFIG_HOME="$PWD/target/install-smoke/c111f856/xdg/config" XDG_CACHE_HOME="$PWD/target/install-smoke/c111f856/xdg/cache" XDG_DATA_HOME="$PWD/target/install-smoke/c111f856/xdg/data" VERNIER_READBACK="$PWD/target/install-smoke/c111f856/readback.ppm" "$PWD/target/install-smoke/c111f856/bin/vernier"
test -s target/install-smoke/c111f856/readback.ppm
```
Expected: exit 0 and a non-empty native framebuffer readback.
- [ ] **Step 6: Activate the managed release atomically and verify rollback**
```bash
scripts/install-local.sh --dry-run "$PWD/target/release-input/c111f856" 0.0.3-alpha c111f856929f6c6370cb4ff5cd094bdb39f699f2
scripts/install-local.sh "$PWD/target/release-input/c111f856" 0.0.3-alpha c111f856929f6c6370cb4ff5cd094bdb39f699f2
readlink -f /home/nilsb/.local/share/vernier/current
readlink -f /home/nilsb/.local/share/vernier/previous
test "$(sha256sum /home/nilsb/.local/share/vernier/current/vernier-app | awk '{print $1}')" = "$(awk -F= '$1 == "sha256_vernier-app" {print $2}' /home/nilsb/.local/share/vernier/current/.vernier-release)"
test "$(sha256sum /home/nilsb/.local/share/vernier/current/vernier-worker | awk '{print $1}')" = "$(awk -F= '$1 == "sha256_vernier-worker" {print $2}' /home/nilsb/.local/share/vernier/current/.vernier-release)"
```
Expected: `current` resolves to `releases/c111f856929f6c6370cb4ff5cd094bdb39f699f2`; `.vernier-release` records `version=0.0.3-alpha`; `previous` still resolves to the September 10 release; hashes pass.
### Task 2: Refresh the live roadmap and release record
**Files:**
- Create: `docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md`
- Modify: `AGENTS.md`
- Modify: `PROJECT_BRIEF.md`
- Modify: `README.md`
- Modify: `docs/LOCAL_LINUX_RELEASE_2026-09-10.md`
- Modify: `docs/APP_ISSUES_AUDIT_2026-09-13.md`
**Interfaces:**
- Consumes: Task 1's exact revision, gate duration, release id, reports, and verification boundary.
- Produces: one present-tense authority separating completed, verified gaps, and deliberate exclusions.
- [x] **Step 1: Add the dated roadmap authority**
Create a document with these exact top-level sections: `Direction`, `Completed baseline`, `Verified gaps`, `Phase 2`, `Phase 3`, `Phase 4`, `Phase 5`, `Deliberate exclusions`, and `Acceptance evidence`. Record the full gate's measured result, the installed release id, and the native-readback/driven-interaction boundary.
- [x] **Step 2: Point current documentation at the new authority**
Update `AGENTS.md` to say the 2026-09-14 plan supersedes older sequencing for what to build next. Correct `PROJECT_BRIEF.md` so multi-body is present-tense capability while assemblies remain excluded. Update `README.md` with the two-binary release build and staged smoke requirement.
- [x] **Step 3: Reconcile stale status statements**
Update the local release record with the new `current` and retained `previous` targets. In the app issues audit, replace the stale “full gate not rerun” statement with the measured result but keep unresolved C++/portable-release findings open.
- [x] **Step 4: Verify wording and commit**
```bash
rg -n "2026-09-14|0.0.3-alpha|17m|previous|driven|readback|spline|AppImage" AGENTS.md PROJECT_BRIEF.md README.md docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md docs/LOCAL_LINUX_RELEASE_2026-09-10.md docs/APP_ISSUES_AUDIT_2026-09-13.md
git diff --check
git add AGENTS.md PROJECT_BRIEF.md README.md docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md docs/LOCAL_LINUX_RELEASE_2026-09-10.md docs/APP_ISSUES_AUDIT_2026-09-13.md
git commit -m "docs: set daily reliability and spline roadmap"
```
Expected: no contradictory present-tense phase status and no whitespace errors.
---
## Phase 2 — Close daily-use reliability gaps
### Task 3: Make every preview generation-owned and disposable
**Files:**
- Modify: `crates/vernier-app/src/preview.rs`
- Modify: `crates/vernier-app/src/scene.rs`
- Modify: `crates/vernier-app/src/server/mod.rs`
- Modify: `crates/vernier-app/src/server/body_tools.rs`
- Modify: `crates/vernier-app/src/body_tools.rs`
- Modify: `crates/vernier-app/src/app.rs`
- Modify: `crates/vernier-app/src/drag.rs`
- Test: `crates/vernier-app/src/tests/preview.rs`
- Test: `crates/vernier-app/src/server/body_tools_tests.rs`
- Test: `crates/vernier-app/src/process_codec/validation_tests.rs`
- Test: `scripts/drive/preview-latest-only.json`
**Interfaces:**
- Consumes: existing feature, body-tool, sketch-point, and push/pull preview paths.
- Produces: `PreviewGeneration(u64)`, request/reply generation matching, and one `discard_preview()` route that never mutates authored document/naming state.
- [ ] **Step 1: Add failing unit tests for stale, failed, and cancelled previews**
Tests must queue generations 1 and 2, deliver generation 2 before 1, and assert only generation 2 can populate `Scene.body_preview_document`; then fail generation 3 and assert both preview mesh and preview document are absent while authored JSON and naming-store JSON equal their pre-request snapshots. Add the ABA case: cancel a point preview, reopen the same sketch/point, deliver the old reply, and assert it is ignored.
- [ ] **Step 2: Run the focused tests and record RED**
```bash
cargo test -p vernier-app preview -- --nocapture
```
Expected: failures demonstrate that replies lack generation identity and that body-tool validation/failure can retain a prior candidate.
- [ ] **Step 3: Implement generation matching**
Use these interfaces:
```rust
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub(crate) struct PreviewGeneration(u64);
#[derive(Default)]
pub(crate) struct PreviewClock {
next: u64,
active: Option<PreviewGeneration>,
}
impl PreviewClock {
pub(crate) fn begin(&mut self) -> PreviewGeneration;
pub(crate) fn accepts(&self, generation: PreviewGeneration) -> bool;
pub(crate) fn discard(&mut self);
}
```
Attach the generation to every disposable preview server request/reply. On validation failure, worker failure, apply, cancel, session close, undo, redo, open, and new document, call the same discard route and restore authored scene geometry. Do not alter the push/pull undo-group semantics; only route its preview cancellation through the shared invalidation rule.
- [ ] **Step 4: Run focused tests and record GREEN**
```bash
cargo test -p vernier-app preview -- --nocapture
cargo test -p vernier-app server::body_tools_tests -- --nocapture
cargo test -p vernier-app process_codec::validation_tests -- --nocapture
```
Expected: all pass with authored document and naming snapshots unchanged in refusal/cancel cases.
- [ ] **Step 5: Add and run the driven lifecycle workflow**
The drive script must create two bodies, rapidly change a feature parameter through at least three values, apply the last value, reopen a tool and cancel, attempt one invalid value, undo, redo, save, and reopen. Assert final volume/faces, timeline, body count, no warnings, and the saved JSON value. Run it twice in separate child processes.
```bash
cargo build --locked --release -p vernier-drive -p vernier-app --bin vernier-worker
target/release/vernier-drive scripts/drive/preview-latest-only.json --out target/drive/preview-latest-only --require-adapter RADV --worker-helper target/release/vernier-worker
```
Expected: exit 0 twice with identical exported-geometry observations.
- [ ] **Step 6: Commit**
```bash
git add crates/vernier-app/src/preview.rs crates/vernier-app/src/scene.rs crates/vernier-app/src/server/mod.rs crates/vernier-app/src/server/body_tools.rs crates/vernier-app/src/body_tools.rs crates/vernier-app/src/app.rs crates/vernier-app/src/drag.rs crates/vernier-app/src/tests/preview.rs crates/vernier-app/src/server/body_tools_tests.rs crates/vernier-app/src/process_codec/validation_tests.rs scripts/drive/preview-latest-only.json
git commit -m "fix: make previews generation-safe and disposable"
```
### Task 4: Refuse unsupported imported-geometry edits before dispatch
**Files:**
- Modify: `crates/vernier-app/src/server/body_scene.rs`
- Modify: `crates/vernier-app/src/process_codec/adapters.rs`
- Modify: `crates/vernier-app/src/process_codec/validate.rs`
- Modify: `crates/vernier-app/src/edit.rs`
- Modify: `crates/vernier-ui/src/toolbar.rs`
- Modify: `crates/vernier-ui/src/registry.rs`
- Modify: `crates/vernier-ui/src/shell/mod.rs`
- Modify: `crates/vernier-ui/src/shell/bodies.rs`
- Modify: `crates/vernier-ui/src/shell/items.rs`
- Test: `crates/vernier-app/src/server/import_step_tests.rs`
- Test: `crates/vernier-app/src/tests/value_card.rs`
- Test: `crates/vernier-app/src/process_codec/scene_tests.rs`
- Test: `crates/vernier-app/src/process_codec/validation_tests.rs`
- Test: `scripts/drive/step-import.json`
**Interfaces:**
- Consumes: compiler `OpaqueTarget(BodyId)` semantics and `ProvenanceKey::Opaque` ownership.
- Produces: lower-layer `vernier_ui::shell::BodyCapabilities { parametric_face_edits: bool, parametric_edge_edits: bool, boolean_target: bool }` on every `BodyRow`, capability-bearing face/edge/body `Selection` values, and readiness explanations used by ribbon, toolbar, command bar, and value card.
- [ ] **Step 1: Write failing capability and readiness tests**
Import a STEP solid and assert push/pull, fillet, chamfer, shell, draft, and split-face are disabled before an edit is produced. Assert Join/Cut/Intersect cannot target the imported body, while moving the imported body and consuming it as a boolean tool remain available. Add a modeled-body positive control.
- [ ] **Step 2: Run the tests and record RED**
```bash
cargo test -p vernier-app import_step -- --nocapture
cargo test -p vernier-app value_card -- --nocapture
```
Expected: imported selections currently look like modeled selections to readiness logic.
- [ ] **Step 3: Propagate and consume capabilities**
Add this UI scene value beside `BodyRow` so the dependency continues downward from app to UI:
```rust
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct BodyCapabilities {
pub parametric_face_edits: bool,
pub parametric_edge_edits: bool,
pub boolean_target: bool,
}
```
Add `capabilities: BodyCapabilities` to `BodyRow`. Add lower-layer serializable `FaceOwnerRow { raw: u64, body: BodyId }` and `EdgeOwnerRow { edge: EdgeRef, body: BodyId }` collections to `SceneView`; `server/body_scene.rs` populates them from the same complete face/edge rosters used by picking, including curved faces. Serialize and bound them through the checked process codec. `selection_of` looks up every picked face/edge owner, combines capabilities conservatively, and copies them into `Selection::Body`, `Selection::Bodies`, `Selection::Faces`, `Selection::Edges`, and `Selection::EdgeChain`. `actions_for` and `readiness_for` consume only `vernier-ui` types.
Disable every operation that would persist an opaque topology reference: push/pull, draft, shell opening, fillet, chamfer, split/replace face, new sketch on face, projection of imported edges/vertices, and use as an extrude up-to support. Keep read-only measure/section/fit actions and imported-body move available. Return exact actionable reasons: `Imported faces cannot be edited parametrically; create a feature from this body or select modeled geometry.` and `Imported bodies can be boolean tools, but not boolean targets; select a modeled target body.` Keep compiler refusals as defense in depth.
- [ ] **Step 4: Run focused tests and the driven import workflow**
```bash
cargo test -p vernier-app import_step -- --nocapture
cargo test -p vernier-app value_card -- --nocapture
cargo test -p vernier-app process_codec -- --nocapture
cargo build --locked --release -p vernier-drive -p vernier-app --bin vernier-worker
target/release/vernier-drive scripts/drive/step-import.json --out target/drive/step-import-capabilities --require-adapter RADV --worker-helper target/release/vernier-worker
```
Expected: UI refusals are asserted by text, no edit dispatch occurs, imported tool use and modeled target edits succeed, and exported geometry is unchanged by refusals.
- [ ] **Step 5: Commit**
```bash
git add crates/vernier-app/src/server/body_scene.rs crates/vernier-app/src/process_codec/adapters.rs crates/vernier-app/src/process_codec/validate.rs crates/vernier-app/src/edit.rs crates/vernier-ui/src/toolbar.rs crates/vernier-ui/src/registry.rs crates/vernier-ui/src/shell/mod.rs crates/vernier-ui/src/shell/bodies.rs crates/vernier-ui/src/shell/items.rs crates/vernier-app/src/server/import_step_tests.rs crates/vernier-app/src/tests/value_card.rs crates/vernier-app/src/process_codec/scene_tests.rs crates/vernier-app/src/process_codec/validation_tests.rs scripts/drive/step-import.json
git commit -m "fix: refuse unsupported imported geometry edits early"
```
### Task 5: Make saved-view and section restore atomic across fresh processes
**Files:**
- Modify: `crates/vernier-doc/src/organisation.rs`
- Modify: `crates/vernier-doc/src/document.rs`
- Modify: `crates/vernier-app/src/server/view.rs`
- Modify: `crates/vernier-app/src/view_controls.rs`
- Modify: `crates/vernier-drive/src/main.rs`
- Modify: `crates/vernier-drive/src/lib.rs`
- Modify: `crates/vernier-drive/src/run.rs`
- Modify: `crates/vernier-drive/src/script.rs`
- Test: `crates/vernier-app/src/tests/m4r_section.rs`
- Test: `crates/vernier-doc/src/document/session_tests.rs`
- Test: `crates/vernier-app/src/process_codec/request_tests.rs`
- Create: `scripts/drive/saved-view-section-write.json`
- Create: `scripts/drive/saved-view-section-read.json`
- Create: `scripts/drive/saved-view-section-wrong.json`
- Create: `scripts/drive-fresh-roundtrip.sh`
- Modify: `scripts/drive/export-refuses-a-foreign-document.json`
- Modify: `scripts/drive/file-commands-actual.json`
- Modify: `scripts/drive/l2-enclosure-wrong.json`
- Modify: `scripts/drive/l2-enclosure.json`
- Modify: `scripts/drive/m1-smoke.json`
- Modify: `scripts/drive/m3-b-project.json`
- Modify: `scripts/drive/m3-b-trim-split.json`
- Modify: `scripts/drive/save-preserves-selection.json`
**Interfaces:**
- Consumes: `SavedView`, `SavedSection { body, origin, normal }`, camera pose restore, and file-command child-process support.
- Produces: document format 41 with `SavedSection.enabled`, validate-then-commit restore of camera plus section plane/offset/flip/cutaway, and a paired-process drive runner. The saved body remains a validity anchor for the document-global visual clip plane; exports remain the complete authored solid.
- [ ] **Step 1: Write failing atomicity and persistence tests**
Save one view for each principal plane, non-zero offset, both flip states, and cutaway on/off. Add `enabled: bool` to `SavedSection`; migrate format 40 to 41 by setting `enabled=true` for every previously present saved section. When “include section” is selected, persist the current plane/offset/flip even if cutaway is off. Corrupt the camera pose of one saved view and assert applying it leaves both camera and section state byte-for-byte unchanged. Save/reopen through a fresh server and assert every valid state round-trips.
- [ ] **Step 2: Run tests and record RED**
```bash
cargo test -p vernier-app m4r_section -- --nocapture
cargo test -p vernier-doc session -- --nocapture
```
Expected: current apply mutates section state before camera validation and lacks fresh-process coverage of all settings.
- [ ] **Step 3: Implement validate-then-commit restore**
Build a local candidate camera and candidate section, validate the body and camera pose first, then assign both in one success branch. Set `cutaway_on` from `SavedSection.enabled`, not from `Option` presence. A failure returns its specific error and changes neither candidate. Keep existing document locking and save conflict behavior unchanged.
- [ ] **Step 4: Add a paired-process drive contract**
Add `--input-root ABSOLUTE_PATH` to `vernier-drive`; `type_input_path` resolves a relative artifact beneath that root with the same traversal/symlink containment checks as `type_path`, but performs no file operation. `scripts/drive-fresh-roundtrip.sh WRITE_JSON READ_JSON OUT ...driver options...` runs the writer once in one OS process at `OUT/write`, then launches a distinct reader process with `--input-root OUT/write`; it repeats the pair under `OUT/pair1` and `OUT/pair2` and compares the reader reports and exported artifacts.
The writer creates the part and named views, sets plane/offset/flip/cutaway, saves, and exits. The reader opens the writer's document plus naming store, applies every view, and asserts camera and clip-plane visual state. STEP/STL assertions must prove the complete authored solid is unchanged by cutaway. Include a missing-body negative reader whose document and exported geometry remain unchanged.
```bash
cargo build --locked --release -p vernier-drive -p vernier-app --bin vernier-worker
scripts/drive-fresh-roundtrip.sh scripts/drive/saved-view-section-write.json scripts/drive/saved-view-section-read.json target/drive/saved-view-section-roundtrip --require-adapter RADV --worker-helper target/release/vernier-worker
scripts/drive-fresh-roundtrip.sh scripts/drive/saved-view-section-write.json scripts/drive/saved-view-section-wrong.json target/drive/saved-view-section-wrong --require-adapter RADV --worker-helper target/release/vernier-worker && exit 1 || test "$?" -eq 1
```
Then run the paired writer with `saved-view-section-wrong.json` as reader and require wrapper exit 1. Expected: both positive reader processes exit 0; the negative reader exits 1 only at its intended missing-body assertion.
- [ ] **Step 5: Run Phase 2 regression and commit**
```bash
cargo test --workspace --exclude vernier-drive
git add crates/vernier-doc/src/organisation.rs crates/vernier-doc/src/document.rs crates/vernier-app/src/server/view.rs crates/vernier-app/src/view_controls.rs crates/vernier-drive/src/main.rs crates/vernier-drive/src/lib.rs crates/vernier-drive/src/run.rs crates/vernier-drive/src/script.rs crates/vernier-app/src/tests/m4r_section.rs crates/vernier-doc/src/document/session_tests.rs crates/vernier-app/src/process_codec/request_tests.rs scripts/drive/saved-view-section-write.json scripts/drive/saved-view-section-read.json scripts/drive/saved-view-section-wrong.json scripts/drive-fresh-roundtrip.sh scripts/drive/export-refuses-a-foreign-document.json scripts/drive/file-commands-actual.json scripts/drive/l2-enclosure-wrong.json scripts/drive/l2-enclosure.json scripts/drive/m1-smoke.json scripts/drive/m3-b-project.json scripts/drive/m3-b-trim-split.json scripts/drive/save-preserves-selection.json
git commit -m "fix: restore saved views and sections atomically"
```
Expected: all workspace tests pass and no locking code changed.
---
## Phase 3 — Make spline sweeps usable
### Task 6: Admit exact open spline paths through document and OCCT sweep validation
**Files:**
- Modify: `crates/vernier-doc/src/sketch.rs`
- Modify: `crates/vernier-kernel/src/evaluate.rs`
- Modify: `crates/vernier-kernel/src/occt.rs`
- Modify: `crates/vernier-occt-sys/include/facade.hpp`
- Modify: `crates/vernier-occt-sys/src/facade.cpp`
- Test: `crates/vernier-doc/src/sketch.rs`
- Test: `crates/vernier-kernel/tests/profile_sweep.rs`
**Interfaces:**
- Consumes: `SketchCurve::Spline`, open `SketchCurve::CubicSpline`, façade `build_chain`, `BRepOffsetAPI_MakePipeShell`, and existing `Bridged`/start/end provenance.
- Produces: structural document validation for exact planar spline paths and kernel/façade geometric validation over the existing `SegmentGeometry`/`ProfileSegment` lowering.
- [ ] **Step 1: Write failing document and kernel tests**
Add fit-point and cubic open path positives with an existing closed rectangular or circular profile. Add explicit refusals for too few distinct points, collapsed control polygons, non-finite controls, closed path curves, self-intersection, and any spline path combined with a guide rail. The analytic cubic parabola uses controls `(0,0)`, `(0,L/3)`, `(a/3,2L/3)`, `(a,L)` and expected length `0.5*sqrt(L*L+4*a*a) + L*L/(4*a)*asinh(2*a/L)`.
- [ ] **Step 2: Run tests and record RED**
```bash
cargo test -p vernier-doc sweep -- --nocapture
cargo test -p vernier-kernel --test profile_sweep -- --nocapture
```
Expected: valid spline paths fail with `SplineNotSupported` before reaching OCCT.
- [ ] **Step 3: Separate structural document checks from exact native geometry checks**
In `vernier-doc`, keep ownership, construction, connectivity, closure, shared-curve, finite-control, and origin checks. For line/arc-only paths retain the current pure-Rust length/tangent precheck; for any `Spline`/open `CubicSpline`, defer only length, start-tangent, and self-intersection verdicts until compile. In `vernier-kernel`, continue lowering exact curves to `SegmentGeometry` and `ProfileSegment` with no resampling.
Inside the existing `make_sweep` C++ exception boundary, build the path with `build_chain`, use `BRepGProp::LinearProperties` for exact wire length, obtain the oriented first `TopoDS_Edge` curve with `BRep_Tool::Curve`, evaluate `D1` at the oriented start parameter, and refuse non-finite/zero tangents or a tangent in the profile plane. Preserve the existing exact self-intersection and result-validity checks. Translate every `Standard_Failure`. Remove the blanket spline-path rejection; keep spline-plus-guide as a named refusal before `SetMode` because one spline edge can contain internal C0 corners.
- [ ] **Step 4: Verify exact geometry and provenance**
```bash
cargo test -p vernier-doc sweep -- --nocapture
cargo test -p vernier-kernel --test profile_sweep -- --nocapture
```
Expected: analytic length/volume tolerances pass; start/end caps and every side face have stable non-opaque provenance across a path control edit.
- [ ] **Step 5: Commit**
```bash
git add crates/vernier-doc/src/sketch.rs crates/vernier-kernel/src/evaluate.rs crates/vernier-kernel/src/occt.rs crates/vernier-occt-sys/include/facade.hpp crates/vernier-occt-sys/src/facade.cpp crates/vernier-kernel/tests/profile_sweep.rs
git commit -m "feat: sweep exact profiles along spline paths"
```
### Task 7: Wire spline sweep preview, apply, editing, persistence, and export
**Files:**
- Modify: `crates/vernier-app/src/server/body_tools.rs`
- Modify: `crates/vernier-app/src/scene.rs`
- Modify: `crates/vernier-app/src/edit.rs`
- Modify: `crates/vernier-app/src/sessions.rs`
- Test: `crates/vernier-app/src/tests/sessions.rs`
- Test: `crates/vernier-app/src/tests/preview.rs`
- Modify: `scripts/drive/functions-sweep.json`
- Create: `scripts/drive/spline-sweep-write.json`
- Create: `scripts/drive/spline-sweep-read.json`
**Interfaces:**
- Consumes: Task 3 preview generations and Task 6 exact sweep support.
- Produces: create-session sweep candidate preview, explicit target body, orientation/scale controls, upstream spline edit recompute, save/reopen, STEP/STL geometry assertions, and separate-process determinism.
- [ ] **Step 1: Write failing session and preview tests**
Select a closed profile and an open spline path through the existing create session; assert a disposable candidate appears before Apply, Cancel removes it, Apply adds exactly one sweep feature to the chosen target, and editing a spline control updates the resulting solid without changing the named start cap. Assert `keep_orientation` and end scale each preview and commit their current value.
- [ ] **Step 2: Run tests and record RED**
```bash
cargo test -p vernier-app sessions:: -- --nocapture
cargo test -p vernier-app preview -- --nocapture
```
Expected: the sweep session can apply but has no generated candidate preview.
- [ ] **Step 3: Add sweep feature-preview construction**
Translate a complete `CreateSweep` session into a disposable `AddSweep` command against a cloned document/naming checkpoint and submit it with the active preview generation. Incomplete slots discard the prior candidate. Apply sends the authored command once; cancel sends none.
- [ ] **Step 4: Add and run the driven acceptance script**
The writer script draws the closed profile and fit-point spline path, chooses `NewBody`, changes orientation and scale rapidly, cancels once, reapplies, exports STEP/STL, edits an upstream spline point, verifies volume/centroid/bounds and named cap continuity, undo/redoes, and saves. The reader script opens that exact saved pair through Task 5's `--input-root`, repeats the geometry/reference/export assertions, and supplies the fresh-process persistence proof. A mutated expected volume in the reader is the negative control.
```bash
cargo build --locked --release -p vernier-drive -p vernier-app --bin vernier-worker
scripts/drive-fresh-roundtrip.sh scripts/drive/spline-sweep-write.json scripts/drive/spline-sweep-read.json target/drive/spline-sweep --require-adapter RADV --worker-helper target/release/vernier-worker
```
Expected: exact exported geometry matches the independent analytic/oracle values and both process frame digests agree.
- [ ] **Step 5: Commit**
```bash
git add crates/vernier-app/src/server/body_tools.rs crates/vernier-app/src/scene.rs crates/vernier-app/src/edit.rs crates/vernier-app/src/sessions.rs crates/vernier-app/src/tests/sessions.rs crates/vernier-app/src/tests/preview.rs scripts/drive/functions-sweep.json scripts/drive/spline-sweep-write.json scripts/drive/spline-sweep-read.json
git commit -m "feat: preview and edit spline sweeps in the shell"
```
### Task 8: Support exact closed spline profiles in sweep
**Files:**
- Modify: `crates/vernier-doc/src/sketch.rs`
- Modify: `crates/vernier-kernel/src/evaluate.rs`
- Test: `crates/vernier-doc/src/sketch.rs`
- Test: `crates/vernier-kernel/tests/profile_sweep.rs`
- Modify: `scripts/drive/spline-sweep-write.json`
- Modify: `scripts/drive/spline-sweep-read.json`
**Interfaces:**
- Consumes: existing `CubicSpline { closed: true }` profile validation and Task 6 curve construction.
- Produces: exact closed cubic-spline profile faces and stable one-curve profile provenance; fit-point `Spline` remains open-only because it has no closed flag.
- [ ] **Step 1: Write failing tests**
Use a closed cubic profile with four valid cubic spans and an open planar spline path. Assert solid count 1, cap area, volume, bounds, and one side provenance per exact profile edge. Refuse fewer than two cubic spans, discontinuous joins, zero-area profiles, self-intersection, and non-finite controls with distinct errors.
- [ ] **Step 2: Run RED**
```bash
cargo test -p vernier-doc closed_spline -- --nocapture
cargo test -p vernier-kernel --test profile_sweep closed_spline -- --nocapture
```
Expected: document validation or kernel profile conversion rejects the spline profile.
- [ ] **Step 3: Admit and evaluate the existing exact representation**
Remove only the spline-profile blanket guard. Reuse `validate_profile` for closure/construction/repetition and the native chain builder for exact wire creation; do not tessellate or mint line segments. Preserve profile curve identity as the side-face provenance parent.
- [ ] **Step 4: Run GREEN and extend the driven workflow**
```bash
cargo test -p vernier-doc closed_spline -- --nocapture
cargo test -p vernier-kernel --test profile_sweep closed_spline -- --nocapture
scripts/drive-fresh-roundtrip.sh scripts/drive/spline-sweep-write.json scripts/drive/spline-sweep-read.json target/drive/spline-sweep-closed-profile --require-adapter RADV --worker-helper target/release/vernier-worker
```
Expected: unit/kernel tests and the exact closed-profile GUI branch pass.
- [ ] **Step 5: Commit**
```bash
git add crates/vernier-doc/src/sketch.rs crates/vernier-kernel/src/evaluate.rs crates/vernier-kernel/tests/profile_sweep.rs scripts/drive/spline-sweep-write.json scripts/drive/spline-sweep-read.json
git commit -m "feat: sweep closed cubic spline profiles"
```
---
## Phase 4 — Add spline-section lofts and complete L3
### Task 9: Admit exact spline sections and preserve ruled-loft provenance
**Files:**
- Modify: `crates/vernier-doc/src/sketch.rs`
- Modify: `crates/vernier-kernel/src/evaluate.rs`
- Modify: `crates/vernier-kernel/src/occt.rs`
- Modify: `crates/vernier-occt-sys/src/facade.cpp`
- Test: `crates/vernier-doc/src/sketch.rs`
- Test: `crates/vernier-kernel/tests/profile_loft.rs`
- Modify: `scripts/drive/functions-loft.json`
**Interfaces:**
- Consumes: ruled loft, exact chain builder, equal-curve-count compatibility, `LoftSpanRow`, and OCCT generated/modified history.
- Produces: spline-section acceptance with deterministic reorientation and OCCT-history-derived `GeneratedFace`/`Bridged` provenance.
- [ ] **Step 1: Write failing section and provenance tests**
Cover line-to-cubic and cubic-to-cubic ruled spans with equal curve counts; reverse and rotate one section's input order and assert the same physical face parents. Refuse unequal counts, mixed closure, repeated ids, self-intersection, coincident offsets, and a section whose curve compatibility changes after edit.
- [ ] **Step 2: Run RED**
```bash
cargo test -p vernier-doc loft -- --nocapture
cargo test -p vernier-kernel --test profile_loft -- --nocapture
```
Expected: valid spline sections fail with `SplineNotSupported`.
- [ ] **Step 3: Remove the blanket guard and bind history after orientation**
Pass each exact section chain to the existing native builder. After native wire compatibility/reorientation, map every emitted side face to its oriented lower/upper source edges using OCCT `Generated`/`Modified` history; emit `Bridged { lower, upper }` only when both sources are proven. Refuse missing, duplicate, or ambiguous history rather than pairing by traversal order.
- [ ] **Step 4: Verify exact ruled geometry and deterministic naming**
```bash
cargo test -p vernier-doc loft -- --nocapture
cargo test -p vernier-kernel --test profile_loft -- --nocapture
cargo build --locked --release -p vernier-drive -p vernier-app --bin vernier-worker
target/release/vernier-drive scripts/drive/functions-loft.json --out target/drive/functions-loft-spline --require-adapter RADV --worker-helper target/release/vernier-worker
```
Expected: separate processes agree on volume, bounds, face count, and provenance keys after section reorientation.
- [ ] **Step 5: Commit**
```bash
git add crates/vernier-doc/src/sketch.rs crates/vernier-kernel/src/evaluate.rs crates/vernier-kernel/src/occt.rs crates/vernier-occt-sys/src/facade.cpp crates/vernier-kernel/tests/profile_loft.rs scripts/drive/functions-loft.json
git commit -m "feat: loft exact spline sections with history naming"
```
### Task 10: Complete the L3 rod-holder acceptance workflow
**Files:**
- Create: `scripts/drive/l3-rod-holder-write.json`
- Create: `scripts/drive/l3-rod-holder-read.json`
- Create: `scripts/drive/l3-rod-holder-wrong.json`
- Modify: `scripts/check.fish`
- Modify: `crates/vernier-cli/src/selftest.rs`
- Modify: `crates/vernier-cli/src/selftest_tests.rs`
**Interfaces:**
- Consumes: spline sweep, spline-section ruled loft, explicit body targets, edge fillet, direct push/pull, undo/redo, save/reopen, STEP/STL export.
- Produces: `l3-rod-holder` driven gate, independent exported-geometry oracle, cross-process determinism, and a single-fault negative control.
The 2026-09-14 roadmap deliberately redefines L3 from the older 2026-09-06 guided-rail/delete-face workflow to the user's current acceptance: exact spline sweep, spline-section ruled loft, edge fillet, direct edit, and upstream dimension change. Spline-plus-guide remains refused because the measured auxiliary-spine route can hang and one spline edge can contain internal C0 corners. Delete-face remains an existing capability but is not an L3 requirement; smooth guided lofts, G1/G2 continuity, periodic lofts, and arbitrary 3D spline drawing remain later work.
- [ ] **Step 1: Add a failing headless geometry oracle**
Build the rod holder directly through public document commands: swept curved arm, spline-section ruled transition, target-body join, edge fillet, and direct edit. Assert bodies, solids, faces, volume, centroid, bounds, analytic sweep contribution, selected stable references, and the delta after one upstream dimension change.
- [ ] **Step 2: Run RED**
```bash
cargo test -p vernier-cli l3_rod_holder -- --nocapture
```
Expected: the named selftest does not exist or spline loft construction fails.
- [ ] **Step 3: Implement the oracle and GUI scripts**
The writer drive starts empty and creates every feature through the real shell, previews then applies each organic feature, fillets a named edge, push/pulls a named planar face, edits an upstream dimension, undo/redoes, saves, and exports STEP/STL. The reader uses Task 5's paired-process runner to open that exact saved pair in a fresh process and repeat geometry, reference, and export assertions. The negative file differs in one expected stable reference or geometry scalar and must fail only at that step.
- [ ] **Step 4: Add L3 to the gate with explicit negative semantics**
Add the paired positive drive as an expected-zero step and the negative reader as an expected-one step. Keep the same RADV precondition and installed worker helper as the other modeling drives.
- [ ] **Step 5: Run L3, then the full gate**
```bash
cargo test -p vernier-cli l3_rod_holder -- --nocapture
cargo build --locked --release -p vernier-drive -p vernier-app --bin vernier-worker
scripts/drive-fresh-roundtrip.sh scripts/drive/l3-rod-holder-write.json scripts/drive/l3-rod-holder-read.json target/drive/l3-rod-holder --require-adapter RADV --worker-helper target/release/vernier-worker
target/release/vernier-drive scripts/drive/l3-rod-holder-wrong.json --out target/drive/l3-rod-holder-wrong --require-adapter RADV --worker-helper target/release/vernier-worker && exit 1 || test "$?" -eq 1
fish scripts/check.fish
```
Expected: positive exits 0 twice deterministically; negative exits 1 at its single intended expectation; full gate exits 0.
- [ ] **Step 6: Commit**
```bash
git add scripts/drive/l3-rod-holder-write.json scripts/drive/l3-rod-holder-read.json scripts/drive/l3-rod-holder-wrong.json scripts/check.fish crates/vernier-cli/src/selftest.rs crates/vernier-cli/src/selftest_tests.rs
git commit -m "test: gate the L3 spline rod holder workflow"
```
---
## Phase 5 — Strengthen performance and release confidence
### Task 11: Measure representative L2-L4 interaction latency and memory
**Files:**
- Modify: `crates/vernier-cli/src/bench.rs`
- Modify: `crates/vernier-cli/tests/bench.rs`
- Create: `docs/PERFORMANCE_2026-09-14.md`
- Create as ignored evidence: `target/performance/`
**Interfaces:**
- Consumes: L2 enclosure, L3 rod holder, and existing L4/multi-body document fixtures.
- Produces: per-document cold compile, steady tail edit, preview latency, apply latency, peak RSS, feature/face/triangle counts, and recompute ratio in stable JSON.
- [ ] **Step 1: Write failing CLI output-contract tests**
Assert `vernier-cli --bench-workflows --json` emits schema `vernier.performance.v1` with rows `l2-enclosure`, `l3-rod-holder`, and `l4-multibody`; each row has positive `cold_ms`, `tail_edit_ms`, `preview_ms`, `apply_ms`, `peak_rss_bytes`, `features`, `faces`, and `triangles`, plus sample count and machine metadata.
- [ ] **Step 2: Run RED**
```bash
cargo test -p vernier-cli bench_workflows -- --nocapture
```
Expected: the workflow benchmark option/schema does not exist.
- [ ] **Step 3: Implement deterministic fixtures and measurement**
The coordinator launches one fresh child process per fixture and sample because `/proc/self/status` `VmHWM` is process-lifetime monotonic. Each child loads exactly one workflow, performs warmup 1 and one measured cold/preview/apply/tail-edit sequence, reads its own `VmHWM`, and emits one JSON sample; the coordinator runs 5 children per fixture and reports median plus maximum. Use monotonic wall time around public compile/preview/apply entry points. Keep performance measurements out of correctness assertions except the existing broad ceiling and memo ratio.
- [ ] **Step 4: Measure release binaries and identify the dominant cost**
```bash
cargo build --locked --release -p vernier-cli
target/release/vernier-cli --bench-workflows --json > target/performance/workflows.json
```
Expected: valid schema and all metrics positive. `docs/PERFORMANCE_2026-09-14.md` records the machine, commands, raw artifact path, dominant measured cost for each fixture, and any optimization made. Only optimize a path that dominates at least one representative interaction; add a result-equivalence test before changing it and record before/after medians.
- [ ] **Step 5: Verify and commit**
```bash
cargo test -p vernier-cli bench -- --nocapture
git add crates/vernier-cli/src/bench.rs crates/vernier-cli/tests/bench.rs docs/PERFORMANCE_2026-09-14.md
git commit -m "perf: measure representative modeling workflows"
```
### Task 12: Align CI with the local gate and calibrate software rendering
**Files:**
- Modify: `.gitea/workflows/ci.yaml`
- Modify: `scripts/check.fish`
- Create: `scripts/acceptance-gate.json`
- Create: `scripts/run-acceptance-gate.py`
- Create: `scripts/test-acceptance-gate.py`
- Modify: `crates/vernier-drive/src/main.rs`
- Modify: `crates/vernier-drive/src/expect.rs`
- Modify: `crates/vernier-drive/src/run.rs`
- Test: `crates/vernier-drive/src/script.rs`
- Create: `docs/SOFTWARE_RENDERER_2026-09-14.md`
**Interfaces:**
- Consumes: local gate step list, cross-process child mode, PNG golden comparator, and available lavapipe Vulkan ICD.
- Produces: `vernier.acceptance.v1` JSON as the one machine-readable gate manifest, one standard-library Python runner shared by local and CI execution, RADV determinism retention, and a measured lavapipe absolute hot-pixel tolerance.
- [ ] **Step 1: Write failing manifest and tolerance tests**
`scripts/test-acceptance-gate.py` loads `.gitea/workflows/ci.yaml`, `scripts/check.fish`, and `scripts/acceptance-gate.json`; assert both entry points invoke `scripts/run-acceptance-gate.py`, the manifest schema is `vernier.acceptance.v1`, step ids are unique, argv is a JSON string array rather than a shell command, and every negative control has `expected_exit: 1`. Assert golden comparison reports hot pixels and maximum channel delta separately and requires an adapter-specific measured budget.
- [ ] **Step 2: Run RED**
```bash
python3 scripts/test-acceptance-gate.py
cargo test -p vernier-drive golden -- --nocapture
```
Expected: local and CI steps have no shared manifest and tolerance is not adapter-calibrated.
- [ ] **Step 3: Extract and consume the shared gate manifest**
Each `scripts/acceptance-gate.json` step has exactly `id`, `scope`, `argv`, `expected_exit`, `adapter`, and `artifact`; `scope` is `all`, `rad-v`, or `software`. `scripts/run-acceptance-gate.py` uses only `json`, `subprocess`, `pathlib`, and `time`, never `shell=True`; it substitutes `{adapter}`, `{worker}`, and `{out}`, records one JSON result per step, stops on the first unexpected status, and preserves the child's stdout/stderr artifact. `scripts/check.fish` invokes the runner with RADV after the build prerequisites. The existing `.gitea/workflows/ci.yaml` installs `python3` and invokes the same runner with llvmpipe/lavapipe; it uploads timing, reports, PNGs, exported geometry, and negative-control stderr on failure.
- [ ] **Step 4: Run and record the software-renderer calibration**
Run each golden workflow twice under lavapipe in separate processes, compare lavapipe-to-lavapipe for determinism, then compare against RADV. Record adapter strings, driver versions, resolution, sample count, observed maximum hot pixels/channel delta, and the smallest budget that covers all observed runs. Do not claim support if no lavapipe ICD is installed; install/package validation must make that prerequisite explicit and repeat the run.
- [ ] **Step 5: Verify CI parity and commit**
```bash
cargo test -p vernier-drive -- --nocapture
fish scripts/check.fish
git add .gitea/workflows/ci.yaml scripts/check.fish scripts/acceptance-gate.json scripts/run-acceptance-gate.py scripts/test-acceptance-gate.py crates/vernier-drive/src/main.rs crates/vernier-drive/src/expect.rs crates/vernier-drive/src/run.rs crates/vernier-drive/src/script.rs docs/SOFTWARE_RENDERER_2026-09-14.md
git commit -m "ci: align acceptance with the local driven gate"
```
Expected: full local gate passes; manifest comparison proves CI includes every local correctness step and preserves determinism/negative controls.
### Task 13: Investigate generated C++ warnings under sanitizers
**Files:**
- Modify only if evidence identifies a project-owned defect: `crates/vernier-occt-sys/src/facade.cpp`
- Modify only if flags need scoping: `crates/vernier-occt-sys/build.rs`
- Create: `docs/CPP_WARNING_SANITIZER_EVIDENCE_2026-09-14.md`
- Create as ignored evidence: `target/sanitizers/`
**Interfaces:**
- Consumes: current generated `cxxbridge` warnings and OCCT façade tests.
- Produces: exact warning inventory, generated-vs-project ownership, ASan/UBSan results, and a minimal fix only where runtime evidence or source analysis proves a project defect.
- [ ] **Step 1: Capture the warnings reproducibly**
```bash
mkdir -p target/sanitizers
CARGO_TARGET_DIR="$PWD/target/sanitizers/normal" cargo build -p vernier-kernel 2>&1 | tee target/sanitizers/warnings.txt
```
Expected: the report groups every warning by compiler, source path, warning flag, generated/project-owned status, and call path.
- [ ] **Step 2: Build and run sanitizer probes**
Use a separate target directory and C/C++ flags `-fsanitize=address,undefined -fno-omit-frame-pointer`, with matching linker flags. Run kernel façade unit tests, profile sweep, profile loft, STEP import/export, booleans, and the L3 headless oracle. Store complete logs under `target/sanitizers/`.
- [ ] **Step 3: Fix only evidenced project-owned undefined behavior**
For each project-owned finding, first add a focused test reproducing it, verify RED under the sanitizer, implement the smallest ownership/bounds/initialization fix, and verify GREEN. Generated `cxxbridge` false positives are documented with the generator/compiler versions and narrowly scoped warning suppression only when the warning is demonstrably unreachable and sanitizer-clean.
- [ ] **Step 4: Verify and commit**
```bash
cargo test -p vernier-kernel -- --nocapture
cargo test -p vernier-kernel --test profile_sweep -- --nocapture
cargo test -p vernier-kernel --test profile_loft -- --nocapture
git add docs/CPP_WARNING_SANITIZER_EVIDENCE_2026-09-14.md crates/vernier-occt-sys/src/facade.cpp crates/vernier-occt-sys/build.rs
git commit -m "build: document C++ warnings with sanitizer evidence"
```
Expected: sanitizer runs contain no unexplained finding and the normal build contains no newly introduced warning.
### Task 14: Vendor and statically link OCCT for release builds
**Files:**
- Create: `.gitmodules`
- Create as a git submodule: `vendor/occt`
- Modify: `crates/vernier-occt-sys/Cargo.toml`
- Modify: `crates/vernier-occt-sys/build.rs`
- Modify: `crates/vernier-kernel/Cargo.toml`
- Modify: `crates/vernier-app/Cargo.toml`
- Modify: `crates/vernier-cli/Cargo.toml`
- Modify: `crates/vernier-drive/Cargo.toml`
- Create: `scripts/build-vendored-occt.sh`
- Create: `scripts/check-static-occt.sh`
- Modify: `scripts/check.fish`
- Modify: `.gitea/workflows/ci.yaml`
- Modify: `README.md`
- Create: `docs/VENDORED_OCCT_RELEASE.md`
**Interfaces:**
- Consumes: OCCT 7.9.3 source tag `V7_9_3`, CMake, the current exact toolkit list, and the existing `system-occt` Cargo feature.
- Produces: a submodule-pinned OCCT source revision, static release archives built with data-exchange support, vendored-default release linkage, and an explicit system-OCCT development path.
- [ ] **Step 1: Pin the official OCCT source and write a failing linkage check**
Add the official OCCT repository as `vendor/occt` at tag `V7_9_3`; the gitlink records the resolved immutable commit, so no branch or floating tag participates after checkout. `scripts/check-static-occt.sh BINARY...` inspects `readelf -d` and fails if any `DT_NEEDED` entry begins with `TK` or resolves beneath a system OCCT directory. Run it against the current release binaries and record RED.
```bash
scripts/check-static-occt.sh target/release/vernier-app target/release/vernier-worker
```
Expected: non-zero with the currently dynamic `TKernel`/`TK*` dependencies named.
- [ ] **Step 2: Build the minimal static OCCT toolkit set reproducibly**
`scripts/build-vendored-occt.sh OUT` requires the initialized git submodule and configures CMake with Release, position-independent code, no samples/tests/docs, and only the modules needed by `TKernel`, `TKMath`, `TKG2d`, `TKG3d`, `TKGeomBase`, `TKGeomAlgo`, `TKBRep`, `TKTopAlgo`, `TKPrim`, `TKBO`, `TKShHealing`, `TKMesh`, `TKFillet`, `TKOffset`, `TKDESTEP`, `TKDE`, and `TKXSBase`. It installs headers, archives, resources, and license files under `OUT`, records OCCT source SHA plus CMake/compiler options, and uses `SOURCE_DATE_EPOCH` for archive timestamps.
- [ ] **Step 3: Make vendored static linkage the release default**
Keep `system-occt` for normal fast development and CI packages. `vernier-kernel` forwards it to `vernier-occt-sys`; `vernier-app`, `vernier-cli`, and `vernier-drive` forward it through their dependency chain. Update normal local/CI commands to request `--features system-occt`. When the feature is absent, `build.rs` locates the prepared tree from `VERNIER_OCCT_STATIC_ROOT`, emits `rustc-link-search=native=<root>/lib`, links the exact toolkit list with `static=`, and links the remaining platform libraries dynamically. It must fail by name when the root, header, archive, resource, or recorded source revision is absent; it must not silently fall back to system OCCT. The portable build script initializes/builds the vendor tree before invoking Cargo without `system-occt`.
- [ ] **Step 4: Verify static linkage, STEP resources, and deterministic behavior**
```bash
scripts/build-vendored-occt.sh target/vendored-occt
VERNIER_OCCT_STATIC_ROOT="$PWD/target/vendored-occt" cargo build --locked --release -p vernier-app --bin vernier-app --bin vernier-worker
scripts/check-static-occt.sh target/release/vernier-app target/release/vernier-worker
target/release/vernier-drive scripts/drive/local-linux-release.json --out target/drive/static-occt-release --require-adapter RADV --worker-helper target/release/vernier-worker
```
Expected: no OCCT `DT_NEEDED`, STEP read/write resources work, and the existing cross-process release workflow remains deterministic.
- [ ] **Step 5: Commit**
```bash
git add .gitmodules vendor/occt crates/vernier-occt-sys/Cargo.toml crates/vernier-occt-sys/build.rs crates/vernier-kernel/Cargo.toml crates/vernier-app/Cargo.toml crates/vernier-cli/Cargo.toml crates/vernier-drive/Cargo.toml scripts/build-vendored-occt.sh scripts/check-static-occt.sh scripts/check.fish .gitea/workflows/ci.yaml README.md docs/VENDORED_OCCT_RELEASE.md
git commit -m "build: vendor OCCT for static releases"
```
### Task 15: Build and certify the AppImage
**Files:**
- Create: `packaging/appimage/AppRun`
- Create: `packaging/appimage/vernier.desktop`
- Create: `packaging/appimage/vernier.svg`
- Create: `scripts/build-appimage.sh`
- Create: `scripts/smoke-appimage.sh`
- Create: `docs/PORTABLE_LINUX_RELEASE.md`
- Modify: `scripts/check.fish`
**Interfaces:**
- Consumes: Task 14's statically linked release `vernier-app`/`vernier-worker`, Task 12 lavapipe support, and installed-workflow drive scripts.
- Produces: immutable `VernierCAD-<version>-x86_64.AppImage`, manifest/SHA-256, clean-environment dependency audit, native readback, and L1-L4 acceptance evidence using the bundled worker.
- [ ] **Step 1: Write failing package-contract smoke tests**
`scripts/smoke-appimage.sh` must fail unless the artifact has exactly one executable `AppRun`, desktop id `vernier`, product name `VernierCAD`, co-located `usr/bin/vernier-worker`, no host-resolved OCCT library from `ldd`, a manifest with revision/version/hashes, and an extract-and-run mode suitable for CI.
- [ ] **Step 2: Run RED**
```bash
scripts/smoke-appimage.sh target/appimage/VernierCAD-0.0.4-alpha-x86_64.AppImage
```
Expected: the artifact is absent and the smoke exits non-zero with a named missing-artifact error.
- [ ] **Step 3: Implement reproducible packaging**
Build locked release binaries against Task 14's vendored static OCCT tree, stage an AppDir, recursively copy only remaining non-base runtime libraries resolved from the two binaries, set `$ORIGIN`-relative lookup, include licenses, OCCT resources, and fonts, generate a sorted manifest, normalize file mtimes from `SOURCE_DATE_EPOCH`, and invoke the available AppImage builder. `AppRun` resolves its own directory and execs `usr/bin/vernier-app`; the app continues to find its sibling worker. The smoke fails if `readelf -d` finds any OCCT `DT_NEEDED` entry.
- [ ] **Step 4: Certify in a clean Linux environment**
Run the artifact with an empty XDG home and no project build tree on `PATH`, require native readback, and run the L1, L2, L3, and L4 driven workflows against the worker extracted from the AppImage. Verify STEP/STL exports with the independent geometry oracle and compare separate-process reports. Retain the previous managed local release until all checks pass.
- [ ] **Step 5: Add the portable release gate and commit**
```bash
scripts/build-appimage.sh --version 0.0.4-alpha --revision "$(git rev-parse HEAD)"
scripts/smoke-appimage.sh target/appimage/VernierCAD-0.0.4-alpha-x86_64.AppImage
sha256sum target/appimage/VernierCAD-0.0.4-alpha-x86_64.AppImage
git add packaging/appimage/AppRun packaging/appimage/vernier.desktop packaging/appimage/vernier.svg scripts/build-appimage.sh scripts/smoke-appimage.sh docs/PORTABLE_LINUX_RELEASE.md scripts/check.fish
git commit -m "release: certify a portable VernierCAD AppImage"
```
Expected: smoke and acceptance workflows pass in the clean environment; `ldd`/runtime tracing finds no host OCCT dependency.
### Task 16: Final integrated verification and managed release switch
**Files:**
- Modify: `docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md`
- Modify: `docs/LOCAL_LINUX_RELEASE_2026-09-10.md`
- Verify: every source and acceptance artifact changed in Tasks 3-15.
**Interfaces:**
- Consumes: all task commits and phase evidence.
- Produces: final full-gate report, clean AppImage certification, managed local release with rollback, and roadmap statuses backed by exact evidence.
- [ ] **Step 1: Run the complete branch gate from a clean index**
```bash
test -z "$(git status --porcelain)"
fish scripts/check.fish
```
Expected: exit 0; all positive and negative controls behave as declared.
- [ ] **Step 2: Rebuild and stage the final local release**
Build app and worker from the exact final SHA, install them under an isolated prefix, run `file-commands-actual`, `local-linux-release`, `preview-latest-only`, `saved-view-section-roundtrip`, `spline-sweep`, and `l3-rod-holder` with the staged installed worker, then run the native launcher with `VERNIER_READBACK`.
- [ ] **Step 3: Switch the managed launcher only after all staged checks pass**
Run `scripts/install-local.sh --dry-run`, install the final alpha, verify `current`, `previous`, manifest revision, and SHA-256 values, and repeat native readback from `/home/nilsb/.local/bin/vernier`. Do not remove either rollback release.
- [ ] **Step 4: Reconcile roadmap status and commit**
Mark only evidenced work complete. Keep any failed or unavailable software-renderer/AppImage environment check in `Verified gaps` with its exact blocker and never turn it into a success claim.
```bash
git diff --check
git add docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md docs/LOCAL_LINUX_RELEASE_2026-09-10.md
git commit -m "docs: record phased release verification"
```
- [ ] **Step 5: Request whole-branch review**
Review the immutable diff from `c111f856929f6c6370cb4ff5cd094bdb39f699f2` to final HEAD for spec compliance, preview/naming atomicity, imported-body authority, exact spline geometry, provenance, negative controls, release reproducibility, and exclusions. Resolve every Critical and Important finding, rerun the affected tests, and repeat the full gate after the last code change.