Epic #305. The uncovered remainder of #308, which PR #329 closed only in part.
What is wrong
#308's headline reproduction still passes at exit 0 on both pinned engines,
and PR #329 does not fix it. That PR guards Unable to convert, which
OpenSCAD prints for a vector- or **transform-**valued parameter it could
not convert. A scalar dimension taking undef is narrated by nothing at
all.
Measured under both engines the CI matrix pins — apt 2021.01 and the pinned
2026.08.01 snapshot — each source rendered with
openscad --export-format binstl -o out.stl p.scad, each o = undef:
| source |
stderr, both engines |
linear_extrude(o) square([40,30]); |
(nothing) |
linear_extrude(height=o) square([40,30]); |
(nothing) |
cube(o); |
(nothing) |
cube(size=o); |
(nothing) |
cylinder(h=o, d=10); |
(nothing) |
sphere(o); |
(nothing) |
circle(r=o); |
(nothing) |
resize(o) cube(5); |
(nothing) |
linear_extrude(o + 1) square([40,30]); |
WARNING: undefined operation (undefined + number) |
cube(o + 1); |
WARNING: undefined operation (undefined + number) |
cylinder(h = o + 1, d = 10); |
WARNING: undefined operation (undefined + number) |
Every one of them exits 0 and exports a clean, watertight, single-solid mesh
built to a dimension nobody wrote down. #308's own example is the ninth row:
100 mm tall because a name was undefined, and no Unable to convert line
anywhere in stderr.
Why the obvious signals do not work
undefined operation cannot be used on the success path. It was tried in
PR #306 and reverted: holes = 4; echo("holes: " + holes); — + where
str() was meant — prints undefined operation (string + number) beside a
completely correct 272-facet part.
Nor can its operand types. The idea that (undefined + number) marks the
dimension case and (string + number) marks the echo case does not survive
contact with an optional parameter left at its undef default:
o = undef;
echo("holes: " + o); // WARNING: undefined operation (string + undefined)
cube([40,30,6]); // ... beside geometry that is exactly right
Both engines, measured. So undefined as an operand is not evidence that the
value reached geometry.
And the silent rows have no signal at all. For linear_extrude(undef)
there is no warning to key on, no exit code, and nothing in the export.
What this means
Any fix needs a channel other than stderr. partspec lint's tier 2 already
reads the .csg export, which is evaluated before any boolean runs and
is where a substituted default would be visible as a literal — that is the
most promising lead, but it is a lead and not a design, and lint exits 0
whatever it finds, so it is advisory by construction.
Until then this shape is uncovered, and
src/partspec/engines/openscad.py's _SUBSTITUTED_VALUE_MARKERS docstring
says so rather than implying #308 closed.
Epic #305. The uncovered remainder of #308, which PR #329 closed only in part.
What is wrong
#308's headline reproduction still passes at exit 0 on both pinned engines,
and PR #329 does not fix it. That PR guards
Unable to convert, whichOpenSCAD prints for a vector- or **transform-**valued parameter it could
not convert. A scalar dimension taking
undefis narrated by nothing atall.
Measured under both engines the CI matrix pins — apt 2021.01 and the pinned
2026.08.01 snapshot — each source rendered with
openscad --export-format binstl -o out.stl p.scad, eacho = undef:linear_extrude(o) square([40,30]);linear_extrude(height=o) square([40,30]);cube(o);cube(size=o);cylinder(h=o, d=10);sphere(o);circle(r=o);resize(o) cube(5);linear_extrude(o + 1) square([40,30]);WARNING: undefined operation (undefined + number)cube(o + 1);WARNING: undefined operation (undefined + number)cylinder(h = o + 1, d = 10);WARNING: undefined operation (undefined + number)Every one of them exits 0 and exports a clean, watertight, single-solid mesh
built to a dimension nobody wrote down. #308's own example is the ninth row:
100 mm tall because a name was undefined, and no
Unable to convertlineanywhere in stderr.
Why the obvious signals do not work
undefined operationcannot be used on the success path. It was tried inPR #306 and reverted:
holes = 4; echo("holes: " + holes);—+wherestr()was meant — printsundefined operation (string + number)beside acompletely correct 272-facet part.
Nor can its operand types. The idea that
(undefined + number)marks thedimension case and
(string + number)marks the echo case does not survivecontact with an optional parameter left at its
undefdefault:Both engines, measured. So
undefinedas an operand is not evidence that thevalue reached geometry.
And the silent rows have no signal at all. For
linear_extrude(undef)there is no warning to key on, no exit code, and nothing in the export.
What this means
Any fix needs a channel other than stderr.
partspec lint's tier 2 alreadyreads the
.csgexport, which is evaluated before any boolean runs andis where a substituted default would be visible as a literal — that is the
most promising lead, but it is a lead and not a design, and
lintexits 0whatever it finds, so it is advisory by construction.
Until then this shape is uncovered, and
src/partspec/engines/openscad.py's_SUBSTITUTED_VALUE_MARKERSdocstringsays so rather than implying #308 closed.