Repository navigation
feat: deliver the fork delta since the 1106 sync wave - #834
Merged
Blogbotana merged 518 commits intoNov 6, 2026
Merged
Conversation
#244) D08 adds the two export surfaces that D03's publication lacked. Both bind to one exact published document version, reusing the strictly resolved stroke geometry that the SVG export already produced, so fonts and symbols stay BeamCraft's own contributor-original stroke drawing: no font is embedded, referenced or substituted anywhere (AGENTS.md sections 1 and 2). - engine `drawing::pdf`: deterministic single-page PDF 1.4 built from path-painting operators only. No /ID, clock or random field: identical version inputs give identical bytes. Literal-string metadata escapes backslash, parentheses and every non-printable byte as three-digit octal. - engine `drawing::print`: the print pipeline shape. Resolved strokes are laid out 1:1 in paper millimetres, centred on a fixed A4-A0 portrait sheet; a drawing that cannot fit its sheet refuses with LIMIT_EXCEEDED instead of silently rescaling, because a rescaled print would misstate every drawn size against its model-millimetre label. - svg refactor: the resolved stroke groups, bounds and glyph/arrow helpers are shared by all three export surfaces; SVG bytes are unchanged. - CLI `export-drawing` gains `--format svg|pdf` and `--sheet a4|a3|a2|a1|a0` (pdf = print-ready centred sheet page); the drawing contract document module map records the new submodules. Native printer spooling (OS dialogs, CUPS/GDI) stays a desktop-app follow-up; the headless pipeline now ends in the print-ready page. Checks: cargo fmt --all; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; cargo run -p beamcraft-cli -- --version. Engine unit tests cover PDF structure (xref offsets, no /Font), print fit refusals and translation-invariant centring; the CLI end-to-end test prints one published version onto A4 twice and asserts identical bytes, refusals for unknown sheet/format and untouched document heads. Fixes #232 Co-authored-by: axisrow <[email protected]>
* feat: lay plate thickness off the contour plane (A12.1) A plate gains the input thickness_side: top, middle or bottom. The contour stays in the element-local plane z = 0; the side says where the thickness t is laid off from it. Top is the default and keeps every older plate: absent input, absent patch field and absent stored token all read as top, and the top path is bit-identical to A02. The engine lays the plain kernel mesh off with the production rigid map and re-certifies it with its own face table, normals and tolerance, so holes pierce the laid-off band and plane-cut records replay on the laid-off body. The side is stored as the optional steel:plate:thicknessSide token and changes the body cache key and the shape signature. Numbering compares side-blind facts, so two plates differing only in the side share a number. Refs #192 (slice A12.1 of the complex-contour issue; A12.2 and A12.3 follow). * fix: encode the thickness side in the UpdateElement fingerprint An op-id retry that differed only in thickness_side hashed equal and silently replayed instead of refusing with OP_ID_REUSE. The side now rides behind the append-only tag 5, like translation_mm and level, so the fingerprints of older patches are unchanged. --------- Co-authored-by: axisrow <[email protected]>
* feat: fastener definitions and libraries pinned by digest (A14.1) Slice A14.1 of the complete bolt groups card (refs #194): a fastener definition in a fastener library in the A07 format, pinned into the project by content digest, with a synthetic example library and one European DIN set. The group, its placement and the generated holes are the following slices (A14.2-A14.4); this slice adds no element and touches no engine command. - beamcraft_model::fastener: FastenerSpec with the bolt amendment's fields (kind, hex head, nut, washer, hole clearance, standard lengths, thread protrusion, declared mass, washer and hole defaults) admitted by the F04 codes and caps plus the definition's own consistency rules; DefinitionCategory::Fastener. - beamcraft_layer_engine: Definition::Fastener rendered as a kind "fastener" definition at /World/Definitions/Fasteners/FST_<sha256>, re-read and re-admitted like a profile; the mapping descriptor gains the fastener line. - beamcraft_cli: load_fastener reads one fastener library file with the profile loader's budgets, identity and refused-entry reporting, entries under /Library/Fasteners; list-library and show-library take --fastener-library (an alternative to --library/--environment, no environments yet per O-B2); define-fastener pins one entry into the store as an immutable definition, so the model never needs the library file again; add-library refuses fastener records (the shipped file is the provenance record). - assets/libraries/synthetic-fasteners.usda: three contributor-original synthetic entries (washered bolt, bare bolt, screw with hole_mode none). assets/libraries/din-fasteners.usda: M12/M16/M20 hex bolts with DIN EN ISO 4032 nuts and DIN EN ISO 7089 washers, clearances from the EN 1090-2 standard hole series, re-entered as our own data; both recorded in ATTRIBUTION.md. - docs/design/a14-1-fastener-libraries.md records the slice, its decisions and its limits. Acceptance covered here: an unknown designation is refused (NOT_FOUND, never a near match); every shipped value has a recorded source and licence; pinning is proven end to end (the digest the listing predicts is what the store pins, and a fresh process reads the definition with the library deleted). "A group never needs the library after pinning" is completed by A14.3 when groups consume the digest. Co-Authored-By: Claude Code <[email protected]> * fix: stop the public fastener docs linking a private loader item The quality job documents with RUSTDOCFLAGS=-D warnings; the intra-doc link from load_fastener to the private load_bytes_as helper broke that. The link is replaced by the public load_fastener_bytes chain it already describes. Co-Authored-By: Claude Code <[email protected]> * fix: the index checks the fastener definition category like the other two The U03 index guards still knew two definition categories: the kind/category cross-check admitted a fastener-kind definition at a profile path and a section kind at a fastener path, a malformed leaf under /World/Definitions/Fasteners passed unflagged, and a digest-less fastener link target lost its digest diagnostic. DefinitionCategory now maps each bim:definitionKind to its category, all three guards cover Fastener, and the index test carries the fastener cases. Co-Authored-By: Claude Code <[email protected]> --------- Co-authored-by: axisrow <[email protected]> Co-authored-by: Claude Code <[email protected]>
* feat: add the V11 selection switches and the snap panel The model window's status bar now carries the eight selection switches (parts, assemblies, bolts, welds, holes, grids, construction objects, items) and a Snap switch that opens a snap-kind strip above the bar. A switch is picking state only: it is consulted when a pick or a grid hit-test runs and read by nothing else, so it can never move the model or the snapshot token. The engine's pick query carries the switches as a SelectionFilter (a class that is off is left out of the scan without a refusal or a skip entry), and the CLI's pick exposes it as --exclude with the class tokens, so the same filter is reachable headlessly. Placement point resolution is deliberately untouched: it is a tool's point resolution, not a selection, and the tools consume the snap panel's kinds in T01.6. Switches for classes that have no registered snapshot kind yet (bolts, welds, holes, construction objects, items) are accepted and exclude nothing until those kinds arrive. Fixes #197 * docs: update the status bar doc comment to the V11 switches --------- Co-authored-by: axisrow <[email protected]>
… (C04) (#256) Co-authored-by: axisrow <[email protected]>
The C05 acceptance rows of the C03-C06 note (docs/design/c03-c06-acceptance.md) had no executable tests: the offline two-client harness, the retry path after a base mismatch, the exactly-once publication across commit crash windows, the interleaved coordinator publications, forced identity collisions and the bounded failure surface. This adds them as one real-binary suite, offline_clients.rs, without touching production behavior. The suite bounds its element count (32 + 32) because every commit re-verifies the whole model; the 1000 + 1000 sweep stays recorded as out of scope in the note. The coordinator rebase (C03 remainder) and compensation (C06) rows stay specified only: their runtime is deferred after the MVP by owner decision and needs one focused PR per C-package. Refs #160 Co-authored-by: axisrow <[email protected]>
) * bisect: fork-sync without the checkout token line * bisect: fork-sync without the workflows permission * fix: make the fork-sync PAT the only path for workflow-file pushes The previous attempt granted the default token `workflows: write` next to `contents: write`. GitHub rejects that: the whole fork-sync.yml fails startup validation on every push and the schedule cannot start at all (cron dead since 13:27Z, bisected 2026-10-27 - the file without the workflows scope validates, with it does not). The job therefore keeps `contents: write` only. The optional FORK_SYNC_TOKEN secret stays the opt-in path for upstream commits that touch .github/workflows/*: without the secret such a sync stops at the push with the script's exit 4 (a red run is the alarm, per the sync contract); with it, the push carries the account identity and every synced master tip gets a CI verdict. Co-Authored-By: Claude Code <[email protected]> --------- Co-authored-by: axisrow <[email protected]> Co-authored-by: Claude Code <[email protected]>
…271) The drafting card needs geometry primitives that belong to a drawing and never to the model. Add bounded drawn objects (line, polyline, arc, circle, rectangle) as document-owned intent: one full UUIDv7 each, exact paper millimetres, radians, admitted through the same D01 admission and persisted in the closed canonical intent USD under /Drawing/Drafting (schema descriptor extended, canonical re-encode comparison unchanged). The new edit-drafting command replaces the drawn set from the document alone - it takes no model store, proving the card's acceptance that drawn objects never touch the model. Published versions stay immutable: an edit after publication lands as a fresh draft on top and the old version still exports its exact bytes. inspect-drawing reports the drawn objects. Refs #231. Text, hatching, symbols and layers/line types are the later slices; SVG/PDF rendering of drawn objects follows separately. Co-authored-by: axisrow <[email protected]>
* feat: loft a ruled surface between two contours (G13) Two planar contours of equal vertex count form one closed certified solid: straight rulings join the index correspondence, wall quads follow the contour edges (split along the interior diagonal when a skew correspondence folds them non-convex), and the G05 cap triangulations close both faces. The sheet thickness is the distance between the contours themselves, so a bent plate authors its outer and inner bend contours and a lofted plate its two face contours directly. Arc contours carry the proven G05 sagitta bound; cylinder and cone patches are the translated-arc and two-radius lofts, checked in the fixtures against the annular-sector and cone closed forms. Fold lines report the rulings at which an authored contour does not continue straight, and the seal verifies the exact oblique-prism volume for exact translations (Cavalieri). Refs #186: slice 1 of the card; the acceptance gate stays with the issue. * test: seal a folded-wall skewed loft through the interior-diagonal split (G13) Review follow-up: every accepted fixture had convex wall quads, where both diagonals are valid, so the diagonal choice in wall_triangles was untested on the acceptance path. The new fixture (a skewed five-point loft from the seeded random scan over random contours and end frames) seals only through the interior-diagonal branch: with the default split forced, the certification refuses this exact body (checked by mutation). The loft carries independent volume bounds (positive, under its bounding box) and an area floor (the two cap areas), plus the divergence-theorem read-back of the blob. Refs #186. --------- Co-authored-by: axisrow <[email protected]>
…12) (#257) * feat: sweep a member along a polyline path with one mitred section (P12) A polybeam is a member whose axis holds a polyline of three to 1024 element-local points instead of two; the point count alone distinguishes it from the P03 straight member, and the stored `bim:axisPointsMm` attribute and its schema are unchanged. The body is the G12 sweep of the one pinned section along that path: every interior vertex is a shared mitre ring, the end rings stay perpendicular to the end tangents, and the section is parallel-transported with no authored roll. The quantity of record is the effective section area times the polyline path length. G12 corollary 1 carries the acceptance: for a section centred on the path every mitre plane crosses the centroid fibre at the vertex, so the certified mesh volume is area x path length and the mitre angles drop out; the seal cross-checks the mesh against that analytic value before anything is stored. Cuts keep the certified replay volume. Point editing is a typed `UpdateElement` patch field: the complete path replacement, authored as `Change::Axis` on the canonical part of an instance group. Two points straighten a polybeam member, three or more make it a polybeam again; the body, mesh and quantity records follow. `create-polybeam` exposes the command in the CLI, and the BOM and numbering member length becomes the body's own path length. Fixes #187 Co-Authored-By: Claude Code <[email protected]> * style: reflow a polybeam test assertion after the mesh bound fix Co-Authored-By: Claude Code <[email protected]> * fix: clear the member roll when an update bends it into a polybeam An update that bends a rolled straight member into a polybeam kept the authored rollRad the sweep silently ignores; the opinion is now cleared and authored as removed on the canonical part. A patch that names path points on a plate target is refused with UNSUPPORTED_PROPERTY instead of dropping the field, and the module doc no longer links the private commit tail. Co-Authored-By: Claude Code <[email protected]> --------- Co-authored-by: axisrow <[email protected]> Co-authored-by: Claude Code <[email protected]>
…276) * feat: five viewport render modes on user-remappable shortcuts (V16) The engine defines the modes in one query (RenderMode: wireframe, transparent, grey, shaded, selected only); the window keeps one loaded scene and only switches the drawing pipeline, so a mode change never reloads, re-uploads or moves the camera. Selected only reads the chosen element with one engine query into a cached single-instance draw of the mesh the device already holds. Shortcuts live in shortcuts.json in the user configuration directory (BEAMCRAFT_CONFIG_DIR or --config-dir with --store): Ctrl+1 to Ctrl+5 by default, a damaged file keeps the defaults and is reported, and one key never drives two modes. --perf-run gains a phase per mode name, so one run records the frame-time baseline of every mode over the loaded scene. * test: a config directory is accepted with a store window --config-dir now names the shortcuts file's directory in the model window too, so the usage test refuses the combinations that are still invalid: an author without a model and a metrics run with a config directory. * fix: a perf mode phase resets the render mode on its boundary A mode phase left App.mode set after its last frame, so a following phase without a mode of its own drew its frames in that leaked mode while the report labelled them with its own name. The boundary frame now re-states the next phase's mode, or shaded after the last mode phase, so a mixed plan measures what its report says. --------- Co-authored-by: axisrow <[email protected]>
…fixtures (#272) The copy/edit command adapters each carried an identical changed_layers computation; publication::changed_layers replaces those 13 copies. The CLI integration suites each carried a private temp-store fixture, binary runner and JSON accessors; support::StoreFixture now backs them and the shared text/at accessors replace the local copies, keeping command-specific change construction, admission and replay decisions, evidence and id-map harnesses and domain expectations local. Behavior and the CLI output contract are unchanged; the workspace suite passes before and after the refactor. Co-authored-by: axisrow <[email protected]>
* feat: place a member's section about its axis (P14) A straight member's section could only sit centred on its axis. Real detailing needs the section off the axis: a beam flush with a grid line, a column face on a boundary, a solid that starts below a floor slab while its axis stays on the grid. The axis points stay the modelling line; the position moves the material. create-member authors an optional SectionPlacement: plane (left/centre/ right) and depth (front/centre/back) with additive offsets in the rolled local section frame, and start/end offsets along the axis that move the solid's faces, so its length follows and must stay positive. The rotation about the axis stays the existing roll. Columns share the whole path. The default placement keeps the kernel's centred body bit for bit: the P03 vertex expressions are used verbatim when nothing is displaced, so mesh bytes, quantities and signatures of members authored without the fields are unchanged. Position is stored as six optional USD attributes (all six or none, absent = centred); the schema descriptor changes, so older pre-alpha stores are refused without migration, the standing pre-alpha policy. Body-cache keys and the P-Mass gross signature include the placement. Design note: docs/design/p14-section-placement.md. * style: cargo fmt on the P14 end-to-end tests * fix: the kernel checks the P14 offset caps itself The frame-error mapping re-checked the offsets, but it only runs when the frame is refused, so a finite offset past the dimension cap built through a direct kernel call or an untrusted committed store re-evaluated without the model admission. Every builder now admits the offsets (finite, within 1e6 mm) before the frame, with kernel and engine regressions. * fix: adapt the P14 replay to the polybeam member slice --------- Co-authored-by: axisrow <[email protected]>
* fix: use the shared fixture in D07 drafting tests * fix: simplify the G13 finite sweep admission guard
#275) * feat: treat plate contour corners with a chamfer or a rounding (A12.2) Slice A12.2 of the complex-plate-contours task (refs #192, the F04 plate amendment draft section 2). A contour vertex may carry a chamfer (one or two cut distances) or a rounding (a radius) as an authored parameter; the sharp contour stays stored. The evaluator derives the treated outline in f64 (arcs faceted to at most the 0.05 mm chord deviation of the round-hole rule), the kernel validates it with its exact predicates, and arc_deviation_mm reports the largest kept deviation. A treated origin rectangle takes the contour path, so quantities are those of the faceted body under the new kernel-a12-corner-treatment-volume label. A treatment on a corner that does not really turn, and treatments whose consumed lengths reach an edge length, are INVALID_ARGUMENT (field corner); values are bounded like other coordinates, and a derived outline beyond the F04 contour cap is LIMIT_EXCEEDED. Holes and plane cuts replay on the treated outline. The five parallel steel:plate:corner* attributes author together or not at all; copies, instances, rebases, undo and reopen carry them exactly. Numbering compares the evaluated outline, so plates whose treated outlines differ share no number; quantity signatures gain the block only for treated plates, keeping older stores' records current. No UpdateElement patch and no corner editing commands: those are the A12.3 slice. Co-Authored-By: Claude Code <[email protected]> * docs: correct the numbering outline comment and bound arc segments early (review notes) The outline_of comment still said the origin rectangle has no outline fact; a treated origin rectangle carries its treated outline (A12.2). The arc faceting now refuses, before it allocates, a rounding whose segment count alone exceeds the F04 contour cap, so a radius near the coordinate cap cannot expand to hundreds of thousands of points on its way to the same refusal. Co-Authored-By: Claude Code <[email protected]> * test: finish migrating the drafting suite to the shared fixtures The merge of #272 left tests/drawing/drafting.rs on the removed Fixture fields and the unscoped Limits import, so the pick suite - which includes it through a path module - no longer compiled and clippy refused the whole workspace. This is the tail of that refactor, included here to unblock CI; no production code changes. Co-Authored-By: Claude Code <[email protected]> --------- Co-authored-by: axisrow <[email protected]> Co-authored-by: Claude Code <[email protected]>
A12.2 (#275) removed the last Limits use from tests/drawing/drafting.rs but kept the import, so cargo clippy --workspace --all-targets -D warnings fails on master (test target pick) and the fork-sync gate refuses every sync. Remove the dead import. Co-authored-by: axisrow <[email protected]> Co-authored-by: Claude Code <[email protected]>
… A-2 records (A13) (#282) * fix: review follow-ups for the A13 fabrication slice Rebased onto current master, so the A12.2 corner-treatment files that an earlier stale-base push showed as deleted are untouched. The split replay now requires the created half and returns both halves from the head; a combine refuses a parallel axis offset across the survivor's line; the split fingerprint hashes the label bytes; a member target of a polygon or part cut is the documented UNSUPPORTED_GEOMETRY; polygon cuts and part cuts carry the member admission; tests cover the split retry, the offset refusal and the member target. * fix: a split replay compares both halves with the probe Presence of the created element alone still replayed a retry that renamed the created half or fired after the target gained a further record. The hold now compares the head created state with the probe created state, so any changed content under the accepted operation id is a reuse refusal, and the a13 suite covers the renamed retry. Co-Authored-By: Claude Code <[email protected]> --------- Co-authored-by: axisrow <[email protected]> Co-authored-by: Claude Code <[email protected]>
…window (V13) (#249) * feat: switch between a perspective and an orthographic camera in one window (V13) The orbit camera gains an orthographic projection next to the perspective one, switched from the ribbon's View tab (PROJECTION group). The switch keeps the target, direction and distance, and the orthographic window always follows the distance (half-height = distance * tan(half FOV)), so the model does not jump and zoom, pan, fit and the distance bounds mean the same in both projections. One shader draws both: params.x carries the field angle in perspective and the half-height in orthographic, so the lateral division is unchanged; the mode slot picks the w (view depth vs 1) and the depth mapping. The pick ray follows the image: under the orthographic projection every pixel is a parallel ray along -back from the laterally offset eye point, so what a click selects and where a placement lands keep matching what is on screen (acceptance). The camera uniform grows from 80 to 96 bytes for the mode slot. Closes #198 Co-Authored-By: Claude Code <[email protected]> * fix: pass the selection filter to pick_in in the orthographic click test The rebase onto master kept two-argument pick_in calls; the T02 helper integration added the SelectionFilter argument. ALL matches the sibling click test's semantics. Co-Authored-By: Claude Code <[email protected]> --------- Co-authored-by: axisrow <[email protected]> Co-authored-by: Claude Code <[email protected]>
A cumulative fork branch once rolled back other people's upstream commits on its way to a mirror PR. The gate makes that failure mode impossible to repeat silently: it refuses a branch whose base is behind upstream/master (printing how many upstream commits are missed), refuses deletions of files that exist in upstream/master unless each path is passed to --allow-delete, and prints a changed-files and +/- lines summary otherwise. The script is read-only bash + git; CONTRIBUTING.md gains the matching step 8. Co-Authored-By: Claude Code <[email protected]>
…from Apibim-com/bug/r01-, Display foreign references instead of validating, bug: admit references under model-sized USD budg
git detects renames by default, so an upstream file moved to a new path showed as R and slipped past the deletion check of upstream-pr-gate.sh; the old path vanished from upstream without hitting --allow-delete. Pass --no-renames so the moved-from path lands in the deleted list. Co-Authored-By: Claude Code <[email protected]>
…er interface (Apibim-com#733), Install the software Vulkan prerequisite for Lin, Show axis labels and readable actions in the grid editor, Complete T01.6 local grid line tools and interactiv The workbench conflict was resolved by the owner's decision to adopt the upstream Inter interface wholesale (apps/beamcraft-ui/src/app/workbench.rs taken from upstream); every other file follows the fork-sync contract.
The expanded story approved the numbering once, so the staleness discipline — the preview going stale exactly when an edit changes the numbering, and the re-approval cycle — had no expanded-story coverage. The renumbering story now runs as the file's thirteenth test in its own published store, keeping the numbered frame's recorded counts and the per-test budget intact. It proves a preview never writes the store and a fresh preview is not stale, that a rename leaves the numbering fresh while a thickness edit that splits the group goes stale with a new token, that the fresh token approves exactly the new marks and that a copy stales the preview again for the second approval, and that the published head is exactly the approved numbering. The matrix gains the executed A08 row and the Number-it trace names it. Co-authored-by: axisrow <[email protected]>
V19 slice 2: bolt-group editing from the bench.
…ough the 1104/1105 delta waves Connect the fork's master to the upstream history it has tracked only by content since the steel wave: every upstream commit from Apibim-com#758 through the Apibim-com#830/Apibim-com#832 delta merges lands here with its own SHA and parents. The merged tree equals the fork master tree byte for byte — the fork already carries every upstream change by content (Apibim-com#758-Apibim-com#827 through the 11-03 absorb, the delta waves through the deliveries themselves) — so this merge changes no fork behaviour; it restores the shared ancestry. The fork-only form stays: the recorded-gate ci.yml, the no-Python rule (the upstream ci tooling does not ride along; card #385 decides its fate), the fork policy documents and the fork's newer fixes that the upstream copies predate. The accumulated fork delta leaves as the next single batch pull request toward upstream. BeamCraft-Test-Gate: {"version":1,"commit":"e692e6a5749f02be7e21f0d64911e91a098494fa","tree":"80f09fd5052e4b62b864f6de3212f27e9bf52f2e","base":"e692e6a5749f02be7e21f0d64911e91a098494fa","merge_parent":"66ee62e2446f93eab7d5bf7d011dc7e39818ff44","os":"macos","arch":"aarch64","scope":"full","ui_independent_reason":null,"ui_tested":true,"rustc":"rustc 1.99.1 (58cf8f9c3 2026-10-13)","cargo":"cargo 1.99.1 (5f94df478 2026-08-27)","nextest":"cargo-nextest 0.9.149 (3fe4ef319 2026-10-20)\nrelease: 0.9.149\ncommit-hash: 3fe4ef31972f24e0c94cfe291718eab0fe678260\ncommit-date: 2026-10-20\nhost: aarch64-apple-darwin","expected_tests":2608,"executed_tests":2608,"ignored_tests":["beamcraft-test-gate::process::process_fixture"],"source_unchanged":true,"stages":[{"name":"format","exit_code":0,"elapsed_ms":4144,"timed_out":false},{"name":"clippy","exit_code":0,"elapsed_ms":18125,"timed_out":false},{"name":"prepare","exit_code":0,"elapsed_ms":52512,"timed_out":false},{"name":"tests","exit_code":0,"elapsed_ms":229789,"timed_out":false},{"name":"doctests","exit_code":0,"elapsed_ms":1802,"timed_out":false},{"name":"docs","exit_code":0,"elapsed_ms":7032,"timed_out":false}]}
The expanded story copied parts as independent copies but never exercised copy-instance, so the delivered instancing feature had no expanded-story coverage. The instance story now runs as the file's fourteenth test in its own published store, keeping the numbered frame's recorded counts and the per-test budget intact. It proves an instance shares its definition's body — a hole and a plane cut applied to the definition ride the whole group with identical local meshes, cuts, hole records and quantities (the exact kept half-space minus the hole), while every member keeps its own name and placement, and publication preserves the shared body state and both identities. The matrix gains the executed U11 row and the model-frame trace names it. Co-authored-by: axisrow <[email protected]>
…le arms (#502) The gpu() helper has panicked on a missing adapter since the recorded-gate delivery, so the `let Some(gpu) = gpu() else { return }` arms at its call sites were dead code that made the suite look like it skips without a GPU - the exact shape the Apibim-com#768 review follow-up quotes. Return Gpu directly and drop the arms; the fail-closed behaviour is unchanged and every pixel assertion still runs behind a real adapter. Co-authored-by: axisrow <[email protected]>
The test-coverage follow-up mirrored in #500 (item 5) names the thin V14 unit coverage: the three existing filter tests pinned `ne` on one missing attribute and one wrong literal type. Pin the rest of the comparison contract: a missing name, class or attribute value satisfies no operator on any field class, an absent target never matches, incompatible literal types and field operators refuse rule admission, and Dataset::new refuses a target value that disagrees with its definition. Test-only: no product code, no contract change, no weakened check. Co-authored-by: axisrow <[email protected]>
Issue Apibim-com#239 asks for a CI wall median of three minutes; the measurement shows the windows test chain (plan -> nextest archive -> slowest shard -> verdict) pins every run at 550-680 s, so the matrix cannot stay inside the ci.yml wall at any tuning. Carry the recorded-gate policy change over: ci.yml keeps plan, the two quality lanes, WASM, licences, the cold release gate and the verdict; ci-platform.yml runs the complete workspace on all three operating systems on every integration push, nightly and manual dispatch with the same archives, partitions, budgets and evidence. The verdict accepts the platform hand-off and no longer requires the test-archive/test jobs. The platform track also restores macOS partition 6/6, which the five-shard tuning of the old wall silently dropped: the canonical inventory is six partitions, so the sixth bucket of macOS tests never executed in CI. The track has no wall-time pressure, so the queued sixth shard costs minutes nightly, not coverage. Local receipt discipline is unchanged: ci-plan still fails closed without a current report on every pull request, push and dispatch. Co-authored-by: axisrow <[email protected]>
* test: pin member-hole fit and refusal paths at the engine replay The engine-level member-hole suite covered single-record shapes only, so the replay's multi-record and refusal contracts stayed unpinned: nothing proved that two records cut together in ordinal order with an exact closed-form net, that a record meeting another hole refuses the evaluation whole, that mixed record directions have no servable replay, or that the member-side admission refusals (wrong target, counter regression, the 65th record, an unknown update id, curved and polybeam members) carry their typed errors. Independent closed-form expectations, no production change and no weakened check. Part of the member-hole slice of the Apibim-com#769 item 5 test-coverage gap (mirror #500). * chore: retrigger the platform-track checks after the cancelled first run --------- Co-authored-by: axisrow <[email protected]>
…g them (#508) * fix: bound rotated part placements for bolt groups instead of refusing them create-bolt-group refused every group whose parts carry a rotated placement (UNSUPPORTED_GEOMETRY), so an angled brace could never be bolted to a gusset (#505). The admission now turns the conservative bounds with the placement quaternion: a plate contributes its extruded contour box turned by the placement, a member its axis points rotated before the section reach is applied, so every bolt path through the shared world bounds accepts rotated parts. Generated holes stay refused on a rotated plate, whose plate-local frame the world box alone does not give; the hole decision reads full placements now instead of bare translations. * fix: read the plate identity through Placement::is_identity_rotation The generated-hole refusal of a rotated plate re-inlined the xyz == 0 predicate; the documented model method exists for exactly this question and keeps the identity rule defined in one place. --------- Co-authored-by: axisrow <[email protected]>
* bug: start the macOS viewport on the trackpad camera preset The camera preset default was platform-blind: every machine started on the mouse mapping, so a Mac without a wheel button had to switch to the trackpad preset by hand in the side panel after every launch. The default now follows the platform (cfg(target_os = "macos") starts on Trackpad; Windows and Linux keep Mouse), and an explicit side-panel choice still wins within a session. * docs: reword the preset docs to the platform-aware start The module and field docs still named `Mouse` as the default, which the platform-aware start of this same PR made false on macOS; both now describe the start preset the way `Preset::default` does (review finding on #507). --------- Co-authored-by: axisrow <[email protected]>
The platform-aware start preset of #507 came back to the platform value on every launch: an explicit side-panel choice lived only in the window state. The chosen preset is now saved into the user's settings.json (a new optional `camera` field on the session settings, read and written under the settings write lock so it never loses a concurrent appearance edit) and read back when a window opens. Nothing saved - a missing field, a null, or an unreadable file - still starts on the platform default, so a fresh profile keeps the Trackpad start on macOS and Mouse elsewhere. A failed save still applies the choice to the open window and reports the failure where the choice was made, matching how the saved-views write reports into its panel. Co-authored-by: axisrow <[email protected]>
…emoval (#510) The D03 suites covered version-ID reuse, single-version export determinism and stale-source freshness, but never exported a historical version after a newer one was published: every export test used exactly one version, and the lifecycle suite never exported at all. The core #333 acceptance leg — document-version-bound output that never silently rebinds — had no CLI-level proof across two versions pinned to two different archived source revisions. Pin it: publish V1 from a 3000 mm member, rebuild the member 4000 mm, expect freshness `stale`, refresh onto the new revision, publish V2, and re-export both. V1 must reopen from its own archived source with identical bytes and its own source_revision_id; V2 must carry the new geometry and pin; the journal must list each version with its own pin. Removing the live model store entirely must report freshness `unavailable` and still export both versions byte-identically from the archived sources alone. Co-authored-by: axisrow <[email protected]>
ISO 4014 carries 160 mm for M20, but the sample list stopped at 150. The engine's conservative-box grip for a knee-brace joint (a 12 mm gusset and a 100 mm member) needs ~154 mm, so auto selection refused every real bolt for that joint; the captured repro lives in #505. Co-authored-by: axisrow <[email protected]>
The icon acceptance job has not acquired a hosted macOS runner since 2026-11-05 14:48 UTC: every attempt after that (four PRs, including master's platform track) waits out the queue budget and is cancelled with 'The job was not acquired by Runner of type hosted even after multiple attempts', while the status page keeps reporting Actions as operational. macos-latest now resolves to the macOS 26 arm64 pool; this pins the same job to the separate macOS 15 arm64 pool and adds a dispatch trigger so the pin can be probed without waiting for capacity on the saturated pool. No check is skipped: the job runs the same build, packaging and screenshots on a hosted macOS image. Co-authored-by: axisrow <[email protected]>
* fix: draw committed bolt groups in the exported view bundle A committed A14.3 bolt group existed only in the store: the view snapshot built its scene from plates, members, lofted plates and references alone, so export-view bundled no fastener meshes and the native viewport showed the joined parts with invisible bolts (issue #512). Each committed group now joins the snapshot as one Connection-kind scene element: the A14.2 prototype of its pinned fastener definition, placed at every bearing point with the group's own frame and merged into one world-space mesh at the identity pose. Every dimension is the pinned definition's or the placement decision's, and the group's record is only read - the view never rewrites the model. The frame-and-bearings arithmetic of place_bolt_group is now shared with the read side (BoltGroupPlaced::from_stored), so the snapshot re-derives exactly the placement the creation decided, and a group the kernel cannot place or build refuses the snapshot like any committed body that no longer evaluates: never a guess and never a silent gap. * docs: bolt-group placement notes follow the view drawing The A14.3 and A14.4 design notes still said the prototype placements are reported but not drawn; issue 512 made both claims false - the view snapshot now merges the placed A14.2 prototypes into one read-only scene mesh per committed group. The layer itself still stores no derived mesh and no instancing (U11), and the generated-hole records still cut the connected plates' own bodies; the notes now say exactly that. * fix: drop an unused test import from the bolt scene suite The import survived an assertion it no longer serves; the recorded gate denies warnings, so the clippy step refused the head that all 2629 tests otherwise passed. * chore: retrigger the CI cycle after cancelled runs The cancelled in-progress runs left tombstone stubs on the checks board (a 0s quality shard and a pending macOS job from a superseded event); a clean empty commit re-plans the whole cycle from the published receipt. --------- Co-authored-by: axisrow <[email protected]>
* test: pin the two-point section view flow in the window (V09) * test: make the two-point view restart leg rebuild the camera for real --------- Co-authored-by: axisrow <[email protected]>
) * feat: publish and export drawing document versions from the window The native UI could move a loaded drawing document's placed views, but the rest of the D03 document lifecycle stayed CLI-only: nothing in the window published a version, listed the published ones or exported one, so a user could not get a drawing out of the model without leaving the app. #333 names this gap directly: the native UI must drive the headless commands. Add a drawing document window beside the sheet editor, on the same pattern: every action spawns the exact command a user types, and the window computes no engineering geometry. Load reads `inspect-drawing` with the workspace as the model store, so freshness is `current`, `stale` or `unavailable` instead of an invented value. Publish commits the loaded head as a new immutable version through `publish-drawing` with fresh operation, revision, actor and version identities, then reloads so the next publish names the new base. The version list comes from `inspect-drawing-versions`; export writes one exact version's SVG or print-ready PDF through `export-drawing`, which refuses an existing destination on its own. Creating and refreshing a document stay CLI commands, and the window says so instead of hiding it. Tests pin the reply parsing (whole mirror or nothing, versions list, absent versions, missing model store reading as `unknown` freshness) and the exact argument lists of all four commands. The full flow was verified against the real CLI: draft load with `current` freshness, publication, version list, SVG and PDF export, and a stale-head publish refusing with BASE_REVISION_MISMATCH. Refs Apibim-com#333 * fix: earn the drawing window's freshness in default store mode The window named the personal workspace as the freshness model store, but that field is only set when the app was opened with --workspace: in the default --store mode the inspect reply carried freshness `unknown` forever, although the live model on screen is known and comparable. Pass the source store instead — main.rs sets it to the workspace directory in workspace mode, so it names the live model on screen in both modes. Spotted in review on #513. --------- Co-authored-by: axisrow <[email protected]>
* feat: export the accepted revision as IFC from the menu (V10) The menu's Export section shows its IFC row as a disabled placeholder; the row now opens a form that commits through the same export-ifc command a user types. The output path is suggested next to the model and stays editable, the overwrite switch travels as --overwrite, and the reply is the CLI's: a refusal is shown on the page and writes nothing, a success reports the command's own counts, status and file digest. A V10 slice for the ribbon/menu acceptance: every button runs the engine command. Unit tests pin the exact argv, the local refusals and the report summary; an end-to-end test clicks the menu rows and the form, runs the real CLI process and requires the written file to equal the one the same command writes by hand, the refusal to reach the page without touching the file, and the store to stay unchanged. * fix: keep the IFC menu row on the Export page only The enabled match keyed on the label alone, so the Import page's IFC row turned clickable too: its click pre-opened the export form under a page that never draws it and cleared the form's note for the next Export visit. The row now enables on the Export section, and a test pins both pages: the Import click neither opens the form nor leaves the page, the Export click does. --------- Co-authored-by: axisrow <[email protected]>
…editor (#518) * feat: lay, change and remove a drawing document sheet from the sheet editor The sheet editor dead-ended on a sheetless document: it told the user to lay a sheet on with the CLI's edit-sheet command, so the D05 sheet layout could not start from the app. The window now carries the edit-sheet form instead — a size, the two title-block fields and the frame inset — applied or removed through the same command a user types, with fresh operation, revision and actor identities and a reload that re-earns the head and mirrors the form from the sheet the document carries afterwards. The form introduces no defaults of its own: the initial size choice is the command's own print default (a4), an empty inset is the command's own 10 mm default, and empty title-block fields keep the button disabled instead of guessing values for D05's still-open preset decisions. Tests pin the exact argument lists of both edit-sheet forms, the frame field reading empty as the default and refusing non-finite junk, and the whole flow was verified against the built CLI: laying an a4 sheet with default frame on the scratch document, and removing it again, both visible in the inspect reply. Refs Apibim-com#333 * fix: start the sheet form on the command's own print default size The form's size choice came from a plain Default string, so it started empty: the combo box showed a blank selection and, with the title-block fields typed, Apply sheet ran edit-sheet without a size and was refused with "sheet must be a4, a3, a2, a1 or a0" until the user opened the dropdown. Seed the choice on a4 — the command's own print default — and pin it with a test; the size can no longer be empty, because the only writers are the size menu and a loaded document's own sheet. Spotted in review on #518. --------- Co-authored-by: axisrow <[email protected]>
Connect the fork's master to the upstream history delivered since the 1105 wave: upstream commit f00260e ("Deliver the fork delta since the 1105 sync wave (Apibim-com#833)") carries the fork's own E04 IFC input study, contour grips, weld panel, bolt panel, A10 bent plates, expanded alpha scenario and the usd-sdk-differential fixtures back into the shared ancestry with their upstream SHAs and parents. The merged tree equals the fork master tree byte for byte - every upstream change is already present in the fork by content, so this merge changes no fork behaviour; it restores the shared history. The fork-only form stays: the recorded-gate ci.yml, the no-Python rule, the fork policy documents and the fork's newer fixes that the upstream copies predate. The fork delta accumulated since the Apibim-com#833 cut (V09/V10/V13 drawing and sheet work) leaves as the next single batch pull request toward upstream. BeamCraft-Test-Gate: {"version":1,"commit":"f3b4ff252295c9d824e13633987f54aeae659d8f","tree":"087ce0ace2921f323399f2129ea841eae1b0503c","base":"f3b4ff252295c9d824e13633987f54aeae659d8f","merge_parent":"f00260ef8bf0b006b826e3b5c4ca6f247c40a853","os":"macos","arch":"aarch64","scope":"full","ui_independent_reason":null,"ui_tested":true,"rustc":"rustc 1.99.1 (58cf8f9c3 2026-10-13)","cargo":"cargo 1.99.1 (5f94df478 2026-08-27)","nextest":"cargo-nextest 0.9.149 (3fe4ef319 2026-10-20)\nrelease: 0.9.149\ncommit-hash: 3fe4ef31972f24e0c94cfe291718eab0fe678260\ncommit-date: 2026-10-20\nhost: aarch64-apple-darwin","expected_tests":2642,"executed_tests":2642,"ignored_tests":["beamcraft-test-gate::process::process_fixture"],"source_unchanged":true,"stages":[{"name":"format","exit_code":0,"elapsed_ms":1587,"timed_out":false},{"name":"clippy","exit_code":0,"elapsed_ms":379,"timed_out":false},{"name":"prepare","exit_code":0,"elapsed_ms":501,"timed_out":false},{"name":"tests","exit_code":0,"elapsed_ms":191471,"timed_out":false},{"name":"doctests","exit_code":0,"elapsed_ms":3016,"timed_out":false},{"name":"docs","exit_code":0,"elapsed_ms":386,"timed_out":false}]}
…520) Issue 519's defect A captured the knee-brace demo drawing its committed M20 group as bare shanks in the native viewport. On the current master the merged-mesh path the window consumes (export-view, #516) already builds the full stack - washer under head, hex head, shank, nut, washer under nut - at the pinned definition's own dimensions, so the defect no longer reproduces; what the acceptance still lacked is the viewport-side proof on the demo joint with the shipped DIN library, pinned where the owner saw the picture. The new GPU test commits a DIN M20 group on the knee-brace demo joint, reads the framed export the window reads and pins the group element's exact bounds by hand from the library definition: the stack crosses the plate faces, the head tip reaches 3.5 mm past the far face and the nut stack tops out 102 mm past it - geometry a shank-only mesh cannot produce - and the picture renders lit on the background. The length is fixed at a standard 90 mm so the pin reads the stack, not the box-conservative grip of defect B (tracked in 505, owner decision). Co-authored-by: axisrow <[email protected]>
windows-latest currently resolves to the windows-2025-vs2026 image, whose hosts fail the install-action step with the bash startup failure (actions/partner-runner-images#169) before any test starts; reruns of the failed master shards landed on the same image twice. windows-2022 is the previous stable pool and avoids the affected image, mirroring the macos-15 pin for the same class of outage. Co-authored-by: axisrow <[email protected]>
Drawing documents: publish and export per-version drawing documents from the window with export binding tracked across republication and store removal, and the sheet editor lays, changes and removes document sheets. The two-point section view flow is pinned in the window, and the accepted revision exports as IFC from the menu (V10). Committed bolt groups now draw in the exported view bundle. Bolt groups: a placed group is editable from the bench (V19 slice 2), rotated part placements are bounded instead of refused, and the member-hole fit and refusal paths are pinned at the engine replay. The side-panel camera preset persists across restarts and the macOS viewport starts on it. The DIN fastener sample library gains the 160 mm M20 length, and the full fastener stack is pinned in the viewport. The Q01 stories for renumbering staleness, storey assignment and instance bodies run in their own published stores, V14 missing-value comparison and type-refusal admission are pinned, and the viewport GPU adapter is required structurally instead of by unreachable arms.
Blogbotana
approved these changes
Nov 6, 2026
axisrow
added a commit
to axisrow/BeamCraft
that referenced
this pull request
Nov 7, 2026
…pibim-com#835 closing-reference policy BeamCraft-Test-Gate: {"version":1,"commit":"025c17c88a475171dd6a2f8171436bdedfc637e7","tree":"2a6318ef7251f92e952df1cf7ad515e7e89b5b45","base":"025c17c88a475171dd6a2f8171436bdedfc637e7","merge_parent":"1cd0aafb9814e524c3cdd7a023303cb7aaba755a","os":"macos","arch":"aarch64","scope":"full","ui_independent_reason":null,"ui_tested":true,"rustc":"rustc 1.99.1 (58cf8f9c3 2026-10-13)","cargo":"cargo 1.99.1 (5f94df478 2026-08-27)","nextest":"cargo-nextest 0.9.149 (3fe4ef319 2026-10-20)\nrelease: 0.9.149\ncommit-hash: 3fe4ef31972f24e0c94cfe291718eab0fe678260\ncommit-date: 2026-10-20\nhost: aarch64-apple-darwin","expected_tests":2665,"executed_tests":2665,"ignored_tests":["beamcraft-test-gate::process::process_fixture"],"source_unchanged":true,"stages":[{"name":"format","exit_code":0,"elapsed_ms":2863,"timed_out":false},{"name":"clippy","exit_code":0,"elapsed_ms":33237,"timed_out":false},{"name":"prepare","exit_code":0,"elapsed_ms":90681,"timed_out":false},{"name":"tests","exit_code":0,"elapsed_ms":232292,"timed_out":false},{"name":"doctests","exit_code":0,"elapsed_ms":2917,"timed_out":false},{"name":"docs","exit_code":0,"elapsed_ms":7123,"timed_out":false}]}
10 tasks done
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.
What this changes
Delivers the fork delta accumulated since the 1105/1106 sync waves as one batch commit on top
of
master(the single-batch delivery the fork sync policy prescribes). Nothing here changesthe recorded contracts; it is feature, fix, test and harness work that
masterpredates.visibility (
crates/engine/src/filter.rs). A rule is admitted against the schema before anyelement is evaluated — an unknown field, attribute or operator refuses before matching, and
a missing value never satisfies a comparison, not even
ne.window; per-version publish and export land in the window with the export binding tracked
across republication and store removal; the two-point section view flow is pinned in the
window; the drawing canvas work sits in
apps/beamcraft-ui/src/drawing.rs.output path next to the model without clobbering a typed path.
placements are bounded instead of refused, committed bolt groups draw in the exported view
bundle, and the member-hole fit and refusal paths are pinned at the engine replay.
trackpad camera preset.
pinned in the viewport.
published stores so the numbered frame keeps its counts; the alpha scenario and matrix
documents describe the split. V14 missing-value comparison and type-refusal admission are
pinned, and the viewport GPU adapter is required structurally instead of by unreachable arms.
The fork-only policy (its workflow forms,
docs/plan/, visual evidence, fork tooling) is notpart of this delivery; the upstream-owned Python CI tools and workflows are untouched.
How you verified it
cargo fmt --all -- --check(recorded gate stage, exit 0)cargo clippy --workspace --all-targets -- -D warnings(recorded gate stage, exit 0)cargo test --workspace(recorded gate: 2643/2643 runnable tests executed nextest 0.9.149 onmacOS aarch64, plus mandatory doctests and
cargo doc -D warnings, all exit 0; thecommitted tree equals the tested tree, receipt in the runner report)
New behaviour carries tests in the same commit: the export, sheet, bolt-group, filter and
story suites exercise the new paths.
Never-crash
Every model file, IFC/DSTV import and keystroke is untrusted input (
AGENTS.md§4).unwrap/expect/panic!/todo!outside testsunsafeProvenance
Read
AGENTS.md§1 and §2 before adding any asset.ATTRIBUTION.md(the DIN fastener library change extendsthe existing contributor-original synthetic library entry)
Anything a reviewer should look at first
Two UI tests in this delivery fail on Windows with path-separator assumptions. They were
written on macOS, and Windows runners only began actually executing the workspace tests this
week after the fork unblocked its Windows pool, so the failures surfaced there first:
app::tests::the_export_ifc_menu_row_writes_the_same_file_as_the_cli_by_handpanics withBACKEND_UNAVAILABLE("store root is not usable") atapps/beamcraft-ui/src/app.rs:8176.app::export_panel::tests::the_suggested_output_sits_next_to_the_model_and_never_clobbers_a_typed_pathasserts
"/m\\Nave 1.ifc"against"/m/Nave 1.ifc"atapps/beamcraft-ui/src/app/export_panel.rs:287.Both are the same class — native path separators in the new export paths. The Linux and macOS
lanes are green. A follow-up fix can ride the next sync wave, or land directly here if
preferred.