Repository navigation
Single-enumerator refactor: framing lumber has one home - #14
Merged
Merged
Conversation
Bake the single JS framing-member enumerator (web/js/members.js) to a committed JSON artifact via a new node scripts/export_members.mjs, with a --verify mode (mirrors gen_wall_instances.py --verify) and a matching tests/members_export.mjs freshness test. This is the export half of the single-enumerator refactor (Decision 6, docs/design_decisions.md): the Python geometry builder will consume this file instead of re-deriving stud/king/jack/header/cripple/sill/blocking positions.
…framing math seh_lib/wall_builder.py's plain-wall and aperture-panel builders (used by both generate_wall_library.py and every library/modules/<id>/compiler.py) now build framing-lumber FreeCAD boxes from web/assets/lib/members.json via a new boxes_from_members() instead of independently computing stud positions, cripple positions, king/jack heights, header placement, sill/ subheader/blocking geometry. That math has one home now: enumerateMembers() in web/js/members.js. boxes_from_members() is pure Python (no FreeCAD import), so it's covered by a FreeCAD-free test, tests/test_members_boxes.py, which cross-checks the same acceptance numbers tests/enumerate_parity.mjs asserts on the JS side against the identical members.json the Python side reads. Object naming (king_stud_left/right, jack_stud_left/right, etc.) is preserved verbatim via _member_object_name(), since library/modules/*/ expect.yaml pattern-matches on those names for libtools validate-output. Deliberately NOT touched: OSB sheathing. The JS side models it as several strips split around a rough opening; this module still cuts a single full sheet with a hole — a real, pre-existing behavior difference between the two implementations (CAD-AUD-005, already recorded as a known_issue on every aperture module's meta.yaml). members.json still carries 'sheathing' role entries for JS consumers; boxes_from_members() just skips them, so this refactor does not silently pick a winner between the two OSB approaches. stud_positions()/cripple_x_positions() are left defined (still part of seh_lib's exported API, still unit-tested transitively via scripts/gen_library_entries.py's own separate copy) but are no longer called by the geometry builders.
build_lib.py now bakes web/assets/lib/members.json (via node scripts/export_members.mjs) before the freecadcmd geometry step, both in the in-place regenerate path and --verify (which checks freshness the same way it already does for wall_instances.yaml and specs.json — bake_geometry.py always reads the real committed members.json, not a temp copy, so if it's stale the geometry comparison downstream is comparing against stale framing math too). CI: ci.yml's fast lane gets a `node scripts/export_members.mjs --verify` freshness check and runs tests/test_members_boxes.py + tests/members_export.mjs. geometry-verify.yml (the FreeCAD BREP-verify job — the mandatory gate for anything this refactor can't check without FreeCAD) gets a setup-node step and members.js/constants.js/systems.js/export_members.mjs added to its path filter, since those now affect baked geometry too. docs/design_decisions.md Decision 6 and docs/adding_modules.md are updated to describe the new members.json step and explicitly call out what's still NOT single-sourced (OSB sheathing, CAD-AUD-005).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements Decision 6 in
docs/design_decisions.md("derived artifacts aregenerated, not hand-maintained — and kept honest by tests"), specifically its
"still partially built" note that framing math lived in more than one place.
Framing-lumber positions (studs, plates, kings, jacks, headers, cripples,
sills, blocking) now have one home:
enumerateMembers()inweb/js/members.js.scripts/export_members.mjs(new) bakes its output to a committedweb/assets/lib/members.json, with a--verifymode mirroring theexisting
gen_wall_instances.py --verifypattern.seh_lib/wall_builder.py— shared bygenerate_wall_library.pyandevery
library/modules/<id>/compiler.py— now builds FreeCAD box shapesfrom
members.jsonvia a newboxes_from_members(), instead ofindependently recomputing stud/cripple positions, king/jack heights,
header/sill/subheader/blocking placement.
boxes_from_members()is purePython (no FreeCAD import) and is unit-tested without FreeCAD.
build_lib.pybakesmembers.jsonbefore thefreecadcmdgeometry step(both in-place and in
--verify).ci.yml) gets anode scripts/export_members.mjs --verifyfreshnesscheck on every push;
geometry-verify.yml(FreeCAD BREP-verify, themandatory gate this refactor can't check locally — see below) gets the new
files added to its path filter plus a
setup-nodestep, sincebuild_lib.py --verifynow shells out tonode.tests/members_export.mjs(JS:members.json== liveenumerateMembers()for every module) andtests/test_members_boxes.py(Python, FreeCAD-free: the same acceptance numbers
tests/enumerate_parity.mjsalready asserts on the JS side, re-assertedagainst what
boxes_from_members()produces from the identicalmembers.json). Object naming (king_stud_left/right,jack_stud_left/right, etc.) is preserved verbatim sincelibrary/modules/*/expect.yamlpattern-matches on those names forlibtools validate-output.Deliberately NOT touched: OSB sheathing
The JS/3D side models exterior OSB as several rectangular strips split
around a rough opening; the Python/FreeCAD side still cuts a single full
sheet with a rectangular hole. This is a real, pre-existing geometry
difference between the two implementations — already tracked as
CAD-AUD-005 and recorded as a
known_issueon every aperture module'smeta.yaml.members.jsonstill carriessheathingrole entries (so JSconsumers are unaffected);
boxes_from_members()just skips them. Thisrefactor does not silently pick a winner between the two OSB approaches —
consolidating OSB generation is separate follow-up work, noted in
docs/design_decisions.md.No dimensional numbers changed. Every acceptance number in
tests/enumerate_parity.mjs(studs at 0/16/32/46.5in, window king/jack/header/sill/cripple dimensions, etc.) is unchanged and re-verified from the
Python side in
tests/test_members_boxes.pyagainst the samemembers.json.Verified locally (Node 26, Python 3.12, no FreeCAD on this machine)
ci.yml's "JS test suite" step, includingtests/parity.mjsandtests/enumerate_parity.mjs(both still passunchanged — 4/4 and 9/9), plus the new
tests/members_export.mjs(17/17).node scripts/export_members.mjs --verifypasses.python -m pytest tests/test_blocking_math.py tests/test_members_boxes.py(26/26).
tests/test_library_drift.pyandtests/test_generated_entry.pycould not run locally — they import
libtools, which requires a networkpip install from
git+https://github.com/OpenSourceEcology/vcs-librarynot available in this sandbox; unrelated to this change.
python3 -m http.serveroverweb/, confirmedindex.html,js/main.js,assets/lib/specs.json, and the newassets/lib/members.jsonall serve 200.web/js/fcstd.jsis untouched(
git diffconfirms zero changes to that file).What CI must still prove (deferred — no FreeCAD on this machine)
geometry-verify.yml'spython3 build_lib.py --verify --no-thumbsis themandatory gate: it must confirm that regenerating
web/assets/lib/*.brp,volumes.json, andcad_library/*.FCStdfrom the refactoredseh_lib/wall_builder.py(now readingmembers.jsoninstead ofrecomputing framing positions) produces geometry identical to what's
currently committed, for all 15 modules. Since
boxes_from_members()reproduces the exact same box positions the old inline math computed (same
constants, same coordinate convention, verified above against the JS
acceptance numbers), no
.brp/.FCStdchanges are expected — but this PRdoes not (and, without FreeCAD locally, cannot) regenerate or touch those
binary assets itself.
libtools validate-output(also in that job) is thecheck that confirms object naming still matches each module's
expect.yamlrole patterns.Not merging
Opening for review — not merging this myself.