docs: reconcile phased execution plan

This commit is contained in:
2026-09-14 13:14:04 +02:00
parent 584bf303e9
commit c6856c1fa7
4 changed files with 175 additions and 54 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ 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 |
| 2. Refresh roadmap | done | task02_roadmap | 584bf30 + review correction pending commit | approved; no Critical/Important/Minor findings | 2026-09-14 authority; exact L3 row reconciled; release/current/previous evidence; focused `rg` and `git diff --check` pass |
| 3. Preview generations | pending | — | — | pending | — |
| 4. Imported readiness | pending | — | — | pending | — |
| 5. Saved views/sections | pending | — | — | pending | — |
+64 -1
View File
@@ -191,6 +191,69 @@ Exit: 0
- Scope: documentation and task-ledger files only; no source or installed-release
mutation.
## Review correction
Independent documentation review identified one Important inconsistency in the
active L3 success-metric row. `PROJECT_BRIEF.md` now names the exact five-part
acceptance from the 2026-09-14 authority: exact spline sweep, spline-section
ruled loft, edge fillet, direct edit, and upstream dimension change. This removes
the generic `tangency` wording that conflicted with the explicit G1/G2 exclusion.
Files changed by this review delta:
- `PROJECT_BRIEF.md`
- `.superpowers/sdd/task-02-report.md`
Focused verification:
```sh
rg -n "L3|exact spline sweep|spline-section ruled loft|edge fillet|direct edit|upstream dimension change|G1/G2" PROJECT_BRIEF.md docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md
```
Exit: 0
```text
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:26: constrained sketching, save/reopen and recovery, STL/STEP I/O, direct edits,
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:89:promise smooth guided lofts, G1/G2 continuity, periodic lofts, or arbitrary 3D
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:98:an edge fillet, a direct edit, and an upstream dimension change, built through the
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:100:older guided-rail/delete-face L3 composition. Delete-face remains a separate
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:101:existing capability, not an L3 requirement. Spline-plus-guide remains refused for
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:105:sections, not smooth guided lofts, G1/G2 continuity, periodic lofts, or arbitrary
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:110:Strengthen performance and release confidence after L3:
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:113: representative L2, L3, and multi-body L4 fixtures in fresh processes.
docs/PLAN_2026-09-14_daily-reliability-spline-modeling.md:127:roadmap. Smooth guided lofts, G1/G2 continuity, periodic lofts, arbitrary 3D
PROJECT_BRIEF.md:50:| **L3** | Rod-holder bracket, organic transition | exact spline sweep, spline-section ruled loft, edge fillet, direct edit, upstream dimension change |
PROJECT_BRIEF.md:183:| **6** | Loft, sweep, splines, STEP I/O, Rhai scripting | L | L3 |
```
```sh
! rg -n -F '| **L3** | Rod-holder bracket, organic transition | loft, sweep, splines, tangency |' PROJECT_BRIEF.md
```
Exit: 0
```text
(no output; stale L3 row absent)
```
```sh
git diff --check -- PROJECT_BRIEF.md .superpowers/sdd/task-02-report.md
```
Exit: 0
```text
(no output)
```
## Concerns
None. Independent review remains pending as recorded in the ledger.
None after the Important review fix. The controller will review and package this
uncommitted delta.
+1 -1
View File
@@ -47,7 +47,7 @@ out of the machine sooner.
| **L0** | Spacer / washer | sketch → extrude → cut → STL |
| **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 |
| **L3** | Rod-holder bracket, organic transition | exact spline sweep, spline-section ruled loft, edge fillet, direct edit, upstream dimension change |
| **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
@@ -6,7 +6,7 @@
**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.
**Tech Stack:** Rust 2024 workspace, `cxx` OCCT façade, egui/wgpu shell, `vernier-drive`, fish/bash release scripts, host `mksquashfs`, and pinned AppImage builder/runtime artifacts fetched with verified SHA-256 values. No new Rust dependency is approved by this plan.
## Global Constraints
@@ -40,7 +40,7 @@
- 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**
- [x] **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
@@ -52,7 +52,7 @@ 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**
- [x] **Step 2: Build both installed binaries and stage them together**
```bash
cargo build --locked --release -p vernier-app --bin vernier-app --bin vernier-worker
@@ -64,7 +64,7 @@ sha256sum target/release-input/c111f856/vernier-app target/release-input/c111f85
Expected: both binaries exist, are executable, and have recorded non-empty SHA-256 values.
- [ ] **Step 3: Exercise the installer under an isolated prefix**
- [x] **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
@@ -73,7 +73,7 @@ scripts/install-local.sh --prefix "$PWD/target/install-smoke/c111f856" "$PWD/tar
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**
- [x] **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"
@@ -82,7 +82,7 @@ target/release/vernier-drive scripts/drive/local-linux-release.json --out target
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**
- [x] **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"
@@ -91,7 +91,7 @@ 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**
- [x] **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
@@ -155,8 +155,20 @@ Expected: no contradictory present-tense phase status and no whitespace errors.
- Modify: `crates/vernier-app/src/body_tools.rs`
- Modify: `crates/vernier-app/src/app.rs`
- Modify: `crates/vernier-app/src/drag.rs`
- Modify: `crates/vernier-app/src/edit.rs`
- Modify: `crates/vernier-app/src/headless.rs`
- Modify: `crates/vernier-app/src/process_worker/helper.rs`
- Modify: `crates/vernier-app/src/process_worker/supervisor.rs`
- Modify: `crates/vernier-app/src/server/checkpoint.rs`
- Test: `crates/vernier-app/src/tests/preview.rs`
- Test: `crates/vernier-app/src/tests/m3a_drag.rs`
- Test: `crates/vernier-app/src/tests/m3_b_cut.rs`
- Test: `crates/vernier-app/src/tests/m3_c.rs`
- Test: `crates/vernier-app/src/app_recovery_tests.rs`
- Test: `crates/vernier-app/src/server/body_tools_tests.rs`
- Test: `crates/vernier-app/src/server/direct_tools_tests.rs`
- Test: `crates/vernier-app/src/process_codec/request_tests.rs`
- Test: `crates/vernier-app/src/process_codec/scene_tests.rs`
- Test: `crates/vernier-app/src/process_codec/validation_tests.rs`
- Test: `scripts/drive/preview-latest-only.json`
@@ -181,7 +193,7 @@ Expected: failures demonstrate that replies lack generation identity and that bo
Use these interfaces:
```rust
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, serde::Serialize, serde::Deserialize)]
pub(crate) struct PreviewGeneration(u64);
#[derive(Default)]
@@ -223,7 +235,7 @@ 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 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/edit.rs crates/vernier-app/src/headless.rs crates/vernier-app/src/process_worker/helper.rs crates/vernier-app/src/process_worker/supervisor.rs crates/vernier-app/src/server/checkpoint.rs crates/vernier-app/src/tests/preview.rs crates/vernier-app/src/tests/m3a_drag.rs crates/vernier-app/src/tests/m3_b_cut.rs crates/vernier-app/src/tests/m3_c.rs crates/vernier-app/src/app_recovery_tests.rs crates/vernier-app/src/server/body_tools_tests.rs crates/vernier-app/src/server/direct_tools_tests.rs crates/vernier-app/src/process_codec/request_tests.rs crates/vernier-app/src/process_codec/scene_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"
```
@@ -231,16 +243,24 @@ git commit -m "fix: make previews generation-safe and disposable"
**Files:**
- Modify: `crates/vernier-app/src/server/body_scene.rs`
- Modify: `crates/vernier-app/src/server/mod.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-app/src/picking.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`
- Modify: `crates/vernier-ui/src/shell/ribbon.rs`
- Modify: `crates/vernier-ui/src/shell/value_card.rs`
- Test: `crates/vernier-ui/src/shell/tests.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/tests/drawing.rs`
- Test: `crates/vernier-app/src/tests/sessions.rs`
- Test: `crates/vernier-app/src/tests/m2_c2_repoint.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`
@@ -275,7 +295,9 @@ pub struct BodyCapabilities {
}
```
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.
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 topology-edit capability conservatively, and copies it into `Selection::Faces`, `Selection::Edges`, and `Selection::EdgeChain`. `Selection::Body`/`Selection::Bodies` carry only the primary target body's `boolean_target` capability; tool bodies do not participate in that value, so modeled-target + imported-tool remains ready. `actions_for` and `readiness_for` consume only `vernier-ui` types.
Session slots outrank ordinary selection, so `pick_face_into_session` additionally takes `&SceneView`, resolves the picked face owner, and examines the armed `FeatureInput`. The existing persistent face inputs, `FilletFaces` and face-based `SketchPlane`, swallow but refuse an imported pick with the same actionable explanation instead of adding it to the session. Keep that predicate in one lower-layer rule and test ordinary modeled picks, imported picks, and modeled-target/imported-tool sessions. Extrude up-to is not a `FeatureInput`: `server/mod.rs` must omit opaque imported faces while it populates `SceneView.extrude_faces`, and a modeled-face positive control must prove the value-card ComboBox still offers valid supports.
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.
@@ -294,7 +316,7 @@ Expected: UI refusals are asserted by text, no edit dispatch occurs, imported to
- [ ] **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 add crates/vernier-app/src/server/body_scene.rs crates/vernier-app/src/server/mod.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-app/src/picking.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-ui/src/shell/ribbon.rs crates/vernier-ui/src/shell/value_card.rs crates/vernier-ui/src/shell/tests.rs crates/vernier-app/src/server/import_step_tests.rs crates/vernier-app/src/tests/value_card.rs crates/vernier-app/src/tests/drawing.rs crates/vernier-app/src/tests/sessions.rs crates/vernier-app/src/tests/m2_c2_repoint.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"
```
@@ -436,6 +458,7 @@ git commit -m "feat: sweep exact profiles along spline paths"
- Modify: `scripts/drive/functions-sweep.json`
- Create: `scripts/drive/spline-sweep-write.json`
- Create: `scripts/drive/spline-sweep-read.json`
- Create: `scripts/drive/spline-sweep-wrong.json`
**Interfaces:**
- Consumes: Task 3 preview generations and Task 6 exact sweep support.
@@ -465,6 +488,7 @@ The writer script draws the closed profile and fit-point spline path, chooses `N
```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
scripts/drive-fresh-roundtrip.sh scripts/drive/spline-sweep-write.json scripts/drive/spline-sweep-wrong.json target/drive/spline-sweep-wrong --require-adapter RADV --worker-helper target/release/vernier-worker && exit 1 || test "$?" -eq 1
```
Expected: exact exported geometry matches the independent analytic/oracle values and both process frame digests agree.
@@ -472,7 +496,7 @@ Expected: exact exported geometry matches the independent analytic/oracle values
- [ ] **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 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 scripts/drive/spline-sweep-wrong.json
git commit -m "feat: preview and edit spline sweeps in the shell"
```
@@ -492,7 +516,7 @@ git commit -m "feat: preview and edit spline sweeps in the shell"
- [ ] **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.
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. Preserve the document representation's valid one-span closed cubic. Refuse an invalid `3*n` control layout, collapsed/zero-area profiles, self-intersection, and non-finite controls with distinct errors.
- [ ] **Step 2: Run RED**
@@ -585,8 +609,8 @@ git commit -m "feat: loft exact spline sections with history naming"
- 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`
- Create: `crates/vernier-cli/src/selftest/l3.rs`
- Modify: `crates/vernier-cli/src/selftest/mod.rs`
**Interfaces:**
- Consumes: spline sweep, spline-section ruled loft, explicit body targets, edge fillet, direct push/pull, undo/redo, save/reopen, STEP/STL export.
@@ -620,7 +644,7 @@ Add the paired positive drive as an expected-zero step and the negative reader a
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
scripts/drive-fresh-roundtrip.sh scripts/drive/l3-rod-holder-write.json scripts/drive/l3-rod-holder-wrong.json target/drive/l3-rod-holder-wrong --require-adapter RADV --worker-helper target/release/vernier-worker && exit 1 || test "$?" -eq 1
fish scripts/check.fish
```
@@ -629,7 +653,7 @@ Expected: positive exits 0 twice deterministically; negative exits 1 at its sing
- [ ] **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 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/l3.rs crates/vernier-cli/src/selftest/mod.rs
git commit -m "test: gate the L3 spline rod holder workflow"
```
@@ -663,7 +687,7 @@ 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.
The coordinator launches fresh child processes because `/proc/self/status` `VmHWM` is process-lifetime monotonic. For each fixture and sample, a `cold` child measures its very first load+compile before any warmup and exits. A separate `interactive` child performs one unmeasured warmup, then measures preview/apply/tail-edit, reads its own `VmHWM`, and exits. The coordinator runs 5 cold children and 5 interactive 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**
@@ -787,6 +811,8 @@ Expected: sanitizer runs contain no unexplained finding and the normal build con
- Create: `scripts/build-vendored-occt.sh`
- Create: `scripts/check-static-occt.sh`
- Modify: `scripts/check.fish`
- Modify: `scripts/acceptance-gate.json`
- Modify: `scripts/test-acceptance-gate.py`
- Modify: `.gitea/workflows/ci.yaml`
- Modify: `README.md`
- Create: `docs/VENDORED_OCCT_RELEASE.md`
@@ -807,11 +833,11 @@ 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.
`scripts/build-vendored-occt.sh OUT` requires the initialized git submodule and configures CMake with Release, position-independent code, no samples/tests/docs, and the modules that contain the façade's direct toolkit set `TKernel`, `TKMath`, `TKG2d`, `TKG3d`, `TKGeomBase`, `TKGeomAlgo`, `TKBRep`, `TKTopAlgo`, `TKPrim`, `TKBO`, `TKShHealing`, `TKMesh`, `TKFillet`, `TKOffset`, `TKDESTEP`, `TKDE`, and `TKXSBase`. It derives and records the complete transitive OCCT `EXTERNLIB` closure from the pinned source/install metadata; this includes dependencies such as `TKBool`, `TKXCAF`, `TKLCAF`, `TKVCAF`, `TKV3d`, `TKService`, `TKCAF`, `TKCDF`, and `TKHLR` when required by 7.9.3. It installs headers, every archive in that closure, 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`.
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 every normal local/CI Cargo argv in `scripts/acceptance-gate.json` to request `--features system-occt`, update its contract test to require that feature on normal Cargo steps, and keep `scripts/check.fish` plus `.gitea/workflows/ci.yaml` consuming that manifest. When the feature is absent, `build.rs` locates the prepared tree from `VERNIER_OCCT_STATIC_ROOT`, verifies the recorded direct set and full transitive `EXTERNLIB` closure, emits `rustc-link-search=native=<root>/lib`, and links that complete closure with `static=`. Use the linker platform's archive group/rescan facility for cyclic OCCT archive references, then link non-OCCT platform libraries dynamically. It must fail by name when the root, header, archive, resource, recorded source revision, closure manifest, or member archive 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**
@@ -827,7 +853,7 @@ Expected: no OCCT `DT_NEEDED`, STEP read/write resources work, and the existing
- [ ] **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 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 scripts/acceptance-gate.json scripts/test-acceptance-gate.py .gitea/workflows/ci.yaml README.md docs/VENDORED_OCCT_RELEASE.md
git commit -m "build: vendor OCCT for static releases"
```
@@ -837,6 +863,8 @@ git commit -m "build: vendor OCCT for static releases"
- Create: `packaging/appimage/AppRun`
- Create: `packaging/appimage/vernier.desktop`
- Create: `packaging/appimage/vernier.svg`
- Create: `packaging/appimage/tools.sha256`
- Create: `scripts/fetch-appimage-tools.sh`
- Create: `scripts/build-appimage.sh`
- Create: `scripts/smoke-appimage.sh`
- Create: `docs/PORTABLE_LINUX_RELEASE.md`
@@ -858,25 +886,45 @@ 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**
- [ ] **Step 3: Pin and fetch the packaging tools**
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.
Record and verify exactly these two inputs in `packaging/appimage/tools.sha256`:
- [ ] **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"
```text
ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0 appimagetool-x86_64.AppImage
1cc49bcf1e2ccd593c379adb17c9f85a36d619088296504de95b1d06215aebbf runtime-x86_64
```
Expected: smoke and acceptance workflows pass in the clean environment; `ldd`/runtime tracing finds no host OCCT dependency.
`scripts/fetch-appimage-tools.sh` downloads appimagetool 1.9.1 from `https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-x86_64.AppImage` and the type2 runtime built at source commit `75849dce7cc37e4319b633df1f116ca895c71a12` from `https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-x86_64` into `target/appimage-tools`. The content hashes above, not the mutable cache or URL label, are the artifact identities. The script validates both SHA-256 values before use, accepts a pre-populated offline cache, and refuses a mismatched file rather than replacing or using it silently. Run appimagetool through `APPIMAGE_EXTRACT_AND_RUN=1` and pass the verified runtime explicitly with `--runtime-file`; do not depend on FUSE or an unversioned host tool.
- [ ] **Step 4: 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 only the verified tools from the preceding step. `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 5: Commit the packaging source before building the immutable artifact**
```bash
git diff --check
git add packaging/appimage/AppRun packaging/appimage/vernier.desktop packaging/appimage/vernier.svg packaging/appimage/tools.sha256 scripts/fetch-appimage-tools.sh scripts/build-appimage.sh scripts/smoke-appimage.sh docs/PORTABLE_LINUX_RELEASE.md scripts/check.fish
git commit -m "release: add reproducible VernierCAD AppImage packaging"
test -z "$(git status --porcelain)"
```
The commit produced here is the immutable source revision embedded in and used to build the artifact. Do not modify this checkout while certification is running.
- [ ] **Step 6: Build and certify from a clean detached worktree**
Create a detached worktree at the packaging-source commit, initialize the pinned OCCT submodule there, fetch or validate the AppImage tool cache, and build the AppImage with `--revision` equal to that worktree's exact `HEAD`. 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.
```bash
release_sha="$(git rev-parse HEAD)"
scripts/fetch-appimage-tools.sh target/appimage-tools
scripts/build-appimage.sh --version 0.0.4-alpha --revision "$release_sha"
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
```
Expected: the manifest revision equals the detached worktree's exact source commit, smoke and acceptance workflows pass in the clean environment, and `ldd`/runtime tracing finds no host OCCT dependency. If certification documentation needs evidence values, record those in a later documentation-only commit without relabelling the already-built artifact as that later revision.
### Task 16: Final integrated verification and managed release switch
@@ -889,7 +937,24 @@ Expected: smoke and acceptance workflows pass in the clean environment; `ldd`/ru
- 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**
- [ ] **Step 1: Request whole-branch review before freezing the release source**
Review the immutable diff from `c111f856929f6c6370cb4ff5cd094bdb39f699f2` to current 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 and rerun the affected tests. Rebuild the AppImage after any source change.
- [ ] **Step 2: Reconcile roadmap status and commit the final source**
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"
test -z "$(git status --porcelain)"
```
This commit is the final source SHA. No source or documentation commit may follow the artifact builds below.
- [ ] **Step 3: Run the complete gate from a clean detached worktree at the final SHA**
```bash
test -z "$(git status --porcelain)"
@@ -898,24 +963,17 @@ fish scripts/check.fish
Expected: exit 0; all positive and negative controls behave as declared.
- [ ] **Step 2: Rebuild and stage the final local release**
- [ ] **Step 4: Rebuild the AppImage and stage the local release from that same SHA**
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`.
In the detached worktree, build and certify the static-OCCT AppImage again, then build app and worker from the exact same final SHA and 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**
- [ ] **Step 5: Switch the managed launcher only after all detached and 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.
From the detached worktree, assemble a release directory containing exactly `vernier-app` and `vernier-worker`, then use the installer's positional contract:
```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"
scripts/install-local.sh --dry-run RELEASE_DIR 0.0.4-alpha FINAL_SHA
scripts/install-local.sh RELEASE_DIR 0.0.4-alpha FINAL_SHA
```
- [ ] **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.
Verify `current`, `previous`, `.vernier-release` revision/version/hash fields, the installed files' SHA-256 values, and repeat native readback from `/home/nilsb/.local/bin/vernier`. Do not remove either rollback release. The managed launcher must remain unchanged on any earlier failure.