15 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
cb5def965b |
fix(cad): exports reported success on a failed write
Every file export buffered its output through a BufWriter and never
flushed it. BufWriter flushes on drop and DISCARDS any error it hits
doing so, so a write that fails only when the buffer is pushed to disk --
full disk, revoked permission, a network mount going away mid-export --
returned Ok(()). The status label then said "STL: 11 parts ->
model.stl" while the file on disk was truncated.
Demonstrated before fixing, with a writer that accepts buffered writes
and fails on flush:
without explicit flush -> Ok(())
with explicit flush -> Err("flush failed: disk full")
Three affected paths: PDF (exporters.rs), STL and GLB (workspace.rs).
All now go through exporters::export_to_file, which owns the BufWriter,
flushes it, and maps a failed flush to "<what>: could not write file:
<cause>". Putting the flush in one place is the point -- it was
forgotten three times independently.
The fourth BufWriter, in arch_pdf, writes into a Vec<u8> where flush
cannot fail. Left alone and excluded from the gate with that reason
recorded, rather than churned for uniformity.
Two tests pin the mechanism rather than one exporter: a small write
stays in the buffer, so write_all succeeds and only the flush can report
the failure. One asserts the drop path hides it, the other that an
explicit flush surfaces it. If BufWriter's drop behaviour ever changed,
the first test would fail and tell us the helper is no longer needed.
CI gate added and negative-tested: no BufWriter::new in the CAD module
outside the flushing helper.
Also checked, and found clean: arch_svg still escapes both XML sinks
after the Phase-4 projection rewrite, and all three cargo-deny
exemptions are still live -- removing them makes exactly those three
advisories fire, so none is a stale entry silently widening the policy.
723 lib + 154 integration, 0 failed. All twelve gates pass.
|
||
|
|
03dea4d14f |
fix(cad): remove panics from CAD production paths
Continued auditing the code the KeyCode fix (
|
||
|
|
817ba02625 |
build: drop the unused robius-sms dependency and its two advisories (Phase B)
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
sms / gates (push) Has been cancelled
sms / robius-sms (push) Has been cancelled
sms / android (push) Has been cancelled
sms / nigig-sms (push) Has been cancelled
sms / supply-chain (push) Has been cancelled
doc-engine / engine (push) Has been cancelled
doc-engine / consumer (push) Has been cancelled
nigig-map / test (push) Has been cancelled
nigig-build declared robius-sms and robius-location and called neither:
zero references to robius_sms or robius_location anywhere under
nigig-build/src. That dead dependency pulled in
robius-sms -> polkit -> gio -> glib -> glib-macros -> proc-macro-error
which is the ONLY reason deny-nigig-build.toml carried
RUSTSEC-2024-0370 (proc-macro-error, unmaintained)
RUSTSEC-2024-0429 (glib VariantStrIter unsoundness)
plus an unanswered LGPL-2.1 distribution question about linking polkit
into a shipped mobile binary -- all of it for code that never ran.
Removing the direct dependency alone was NOT enough, which is the part
worth recording. `cargo tree -p nigig-build -i polkit` showed three
paths, not one: the direct declaration, and two more through nigig-core
and nigig-uikit. Both of those also declare robius-sms and also never
use it. All three declarations had to go before polkit left the graph.
nigig-core keeps robius-location: unlike the others it genuinely uses it,
in src/location.rs.
Verified, not assumed:
- before: cargo tree -p nigig-build -i polkit resolved, three paths
- after: polkit, gio and proc-macro-error no longer resolve at all
- cargo deny check -> "advisories ok, bans ok, licenses ok, sources ok"
with two fewer ignores (5 -> 3)
- nigig-build, nigig-core, nigig-uikit and nigig-sms all still compile
- Cargo.lock loses 5 lines
Also in this commit:
- A CI gate so the declarations cannot come back. Deliberately scoped
to robius-sms/robius-location on these three manifests rather than a
blanket `cargo machete`: five other unused dependencies exist here
(chrono, futures, postcard, rand, serde_json) and a gate that is red
on its first run gets switched off. Negative-tested by re-adding the
dependency and confirming the gate fails.
- Three pages carried the same placeholder string telling the user
they were looking at a "RobrixStackNavigationView destination ...
just like SMS conversation screens". That is user-visible UI copy,
not a comment. Replaced with text describing the page. The identical
"This follows the SMS/Home pattern" comment in the same three files
now says what the code does instead of naming another module.
Note the scope limit: 26 of the 29 crates in this workspace declare
robius-sms and never use it. This commit fixes the three that put
advisories on nigig-build. The rest are the same latent problem and
should be swept separately.
|
||
|
|
5e714577fb |
ci: require full 40-character SHAs for git dependency revs
Some checks failed
Payment domain, storage, platform and UI / isolated-payment-tests (push) Has been cancelled
Payment domain, storage, platform and UI / payment-ui-tests (push) Has been cancelled
PDF engine / engine (push) Has been cancelled
PDF engine / makepad-integration (push) Has been cancelled
PDF engine / fuzz (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
nigig-map / test (push) Has been cancelled
Upstream adopted the makepad fork fix (
|
||
|
|
80425bbfb8 |
fix: repair the makepad fork and unblock the build
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
nigig-map / test (push) Has been cancelled
Payment domain, storage, platform and UI / isolated-payment-tests (push) Has been cancelled
Payment domain, storage, platform and UI / payment-ui-tests (push) Has been cancelled
PDF engine / engine (push) Has been cancelled
PDF engine / makepad-integration (push) Has been cancelled
PDF engine / fuzz (push) Has been cancelled
The repository has been uncompilable since the d6d1f99c fork bump. Root cause was in gitdab.com/andodeki/makepad, not here, so the fix went there first: commit 5eda8056 on portallist_flow_adaptive_view. TWO defects, both introduced by the "Update fork to upstream dev 5d4483f" merge, both pure losses rather than intentional changes: 1. widgets/Cargo.toml: the makepad-gltf / makepad-csg / makepad-test dependency lines were relocated from [dependencies] to below [features]. Cargo then parses each as a feature whose value should be an array, giving "invalid type: map, expected a sequence", and the gltf/csg/test/maps features cease to exist. 2. widgets/src/lib.rs: the feature-gated re-export block for those same crates (plus makepad_fast_inflate and makepad_mbtile_reader) was deleted outright. Fixing only the manifest surfaced this as "no `makepad_csg` in the root". Both restored verbatim from 2c5cd97, the last rev that resolved. Neither is a judgement call: the moved lines are byte-identical and the deleted block is copied back unchanged. This repo is then repinned from d6d1f99c to the fixed rev, full 40-char SHA per the pinning convention CI enforces. Verified end to end after removing the local git redirect used during development, so this resolves against the real remote: cargo metadata resolves nigig-build --lib 685 passed cad_integration 154 passed spreadsheet-engine 225 passed doc-engine 53 passed nigig-map (maps feature) compiles Cargo.lock unchanged, --locked passes Also resolved committed conflict markers in two workflow files, which had made nigig-build.yml invalid YAML -- the CI config could not be parsed at all: - nigig-build.yml: kept --include='*.rs' on the by-value-getter gate. Without it the gate scans ARCHITECTURE.md and fails on its own documentation, which is the bug fixed in 4f32b1c. - pdf.yml: kept upstream's side. Enumerating targets via `cargo fuzz list` and failing when the list is empty is strictly better than a hardcoded target list that silently passes vacuously if a target is renamed. That makes four files in three commits now carrying committed conflict markers from this merge. Worth checking how they are reaching main -- `git diff --check` catches exactly this and is already a step in the nigig-build workflow, but it only runs on paths under that workflow's filter. |
||
| 8c9ccb92cc |
Update makepad fork to latest dev branch (d6d1f99c)
Some checks failed
nigig-build.yml / Update makepad fork to latest dev branch (d6d1f99c) (push) Failing after 0s
pdf.yml / Update makepad fork to latest dev branch (d6d1f99c) (push) Failing after 0s
nigig-map / test (push) Has been cancelled
Payment domain, storage, platform and UI / isolated-payment-tests (push) Has been cancelled
Payment domain, storage, platform and UI / payment-ui-tests (push) Has been cancelled
- Sync with upstream commit 5d4483f (latest map improvements + platform updates) - Include location API, audio echo cancellation, bridge-dz overlay - Add new libraries: geodata, map_nav, i_float, i_shape, i_tree, converse, llama vision - Preserve all fork-specific re-exports (gltf, csg, test) - All 102+ map improvements now available: 2D/3D toggle, shadows, labels, overlays, pattern fills |
|||
|
|
36c9c0b0d1 |
fix(cad): both Save buttons reported success when the write failed
Went looking for remaining defects before starting the viewport split and
found silent data loss in the two Save buttons.
cad_store::save_cad_script(&project.id, &source).ok();
self.view.label(..).set_text(cx, "Saved");
save_cad_state(&source, &solid).ok();
self.view.label(..).set_text(cx, "Saved with mesh");
Both discard a Result carrying a real io::Error and then report success
unconditionally. The user is told their work is safe when nothing was
written.
This is reachable, not theoretical. `cad_store::cad_projects_dir()` only
*logs* a `create_dir_all` failure and returns the path anyway, so the
following `fs::write` fails with a genuine error -- which was thrown
away. An unwritable data directory, a full disk or a permissions problem
all render as "Saved".
Save-As had a third silent failure on the same line of reasoning: if
`bake_parts_solid()` returned None the save never even ran, and it still
said "Saved with mesh".
Fixed via save_status_message(what, result), so the label is derived from
the outcome instead of assumed. Three distinct messages now: the caller's
success wording, "Save failed: <cause>" with the underlying error text
preserved, and a specific message for the no-mesh and no-active-project
cases, which were previously indistinguishable from success.
Also replaced the hand-rolled widget borrow in Save-As with
with_viewport, the helper added in 5.2.
Tests assert the two properties that matter: a failed save must not read
as success and must carry its cause through to the user, and a
successful save must use the caller's wording. Negative-tested.
CI gate added and verified in both directions: greps for a discarded
Result on a save/write/persist/store/export call. A dropped Result is
merely untidy in most places; on a save path it is data loss.
These were the only two instances -- the CAD module now has zero
discarded write Results.
685 lib + 154 integration, 0 failed. All seven gates pass.
|
||
|
|
e7a201246c |
ci(cad): scope the source-scanning gates to .rs files
The "No build-time paths used at runtime" gate has been failing since the
commit that introduced it, and I did not notice because I verified the
gate's intent rather than running its exact shell pipeline.
It greps the CAD directory for env!("CARGO_MANIFEST_DIR") and filters out
lines starting with a // comment marker. ARCHITECTURE.md documents this
very rule, so it necessarily names the macro -- and a Markdown list item
is not a // comment, so it survived the filter. The gate failed on its
own documentation.
Both files were added in the same commit (
|
||
|
|
7143b3e798 |
feat(doc): render CRDT-native tables in CrdtDocEditor
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
doc-engine / engine (push) Has been cancelled
doc-engine / consumer (push) Has been cancelled
- projection_layout builds ProjectedTableLayout geometry (fixed 160x28 cells matching the legacy bridge width, merge spans with covered-cell flags) and per-block origins so blocks after a table clear it instead of using the fixed line step - ProjectionRenderer draws the table grid through a new draw_table_border live field and consumes layout block origins for text placement - table_hit_test maps points to merge-anchor cells for future cell editing - doc-engine: split the cramped style-patch line clippy flagged as possible_missing_else; the crate is clippy-clean again - README: tick verified CRDT bridge boxes (dependency, wiring, selected replacement/formatting/table/toolbar migration, font size/color migration) and document the table milestone - CI: add doc-engine workflow (engine tests + clippy -D warnings + nigig-build consumer check/test) and trigger nigig-build CI on doc changes |
||
|
|
667f565ddd |
fix(cad): match arms bound new variables instead of matching KeyCode
Went looking for a clippy ratchet and found that clippy had never run on
this crate at all: two dependencies fail deny-by-default lints, so
`cargo clippy -p nigig-build` aborted before linting nigig-build. Fixing
those two unblocked the crate and immediately exposed a real defect
class.
THE BUG. A bare identifier in a match pattern that is not a known variant
is parsed by Rust as a NEW BINDING that matches everything. Nine such
names were used as KeyCode patterns:
KeyEnter, KeyBackspace, BracketLeft, BracketRight (cad/viewport.rs)
Digit0..Digit9, Equal, LeftBracket, RightBracket,
Apostrophe (doc, invoice, pm)
Real names are ReturnKey, Backspace, LBracket, RBracket, Key0..Key9,
Equals, Quote.
Consequences, in severity order:
- cad/viewport.rs: `BracketLeft => { .. }` is UNGUARDED and sits above
the numeric arms, so it swallowed every remaining key. All
direct-distance-entry input -- digits, '.', '-', ',' -- was
unreachable. The entire DDE feature was dead.
- The guarded ones fired for any key satisfying the guard: pressing Q
while drawing with a non-empty buffer committed the coordinate.
- doc/invoice/project_management: `Digit0 => '0'` swallowed the rest of
the keymap, so every digit and symbol typed produced '0'.
This compiles cleanly and no test catches it. rustc's only signal is
`unreachable_pattern` plus `unused variable: \`Capitalised\`` -- both
buried in the 200+ warnings nobody could see, because clippy never ran.
85 unreachable-pattern warnings before, 1 after (a benign catch-all).
UNBLOCKING CLIPPY. Two deny-level errors in dependencies:
- nigig-uikit user_project_pill.rs: a `for` loop returning on its first
iteration (never_loop). Rewritten as `.next()`.
- spreadsheet-engine formula2.rs: CellRef had an inherent to_string
shadowing Display (inherent_to_string_shadow_display). The naive fix is
a trap: Display::fmt was `f.write_str(&self.to_string())`, which
resolved to the inherent method -- delete it and the same call resolves
to ToString::to_string, which calls Display::fmt, recursing until the
stack overflows. Verified with a standalone repro before fixing. The
body moved into Display; Range got the Display impl it never had.
Tests: keycode_variant_tests names the four correct variants, so it stops
compiling if any is renamed -- the point being that the old code compiled
precisely because the names were wrong. Two formula2 tests pin that
`x.to_string()` and `format!("{x}")` agree, which is what the shadowing
lint exists to protect.
CI gate added and negative-tested: greps for a capitalised
`unused variable`, which is the signature of this bug. Restoring
BracketLeft makes it fire.
625 lib + 154 integration + 225 spreadsheet-engine + 44 doc-engine, 0
failed.
|
||
|
|
80c3f578dd |
ci(cad): gate nigig-build with cargo-deny (Phase 0.4)
Some checks failed
Blocked since Phase 0 because cargo-deny resolves from the lockfile, and unblocked once Cargo.lock was committed in 0.2. I predicted this would surface a large backlog. It did: 5 advisories, 6 licence rejections, 126 git-source errors and 20 unlicensed crates. The triage is the work here, not the config. ONE ACTUAL VULNERABILITY. RUSTSEC-2026-0187: unbounded recursion parsing nested PDF objects in lopdf 0.31, reached via printpdf 0.7. A ~21 KB crafted file aborts the process with SIGABRT, and because it is a stack overflow rather than a panic, catch_unwind cannot contain it. Not exploitable here, and I checked rather than assumed. The advisory is specifically about Document::load*, the parsing entry points. This crate never parses a PDF -- arch_pdf.rs only writes them through printpdf's drawing API, there is no lopdf import anywhere in the workspace, and there is no PDF read path of any kind. There is also no fix in range: printpdf 0.7 pins lopdf 0.31 and `cargo update -p lopdf` moves nothing. Clearing it means printpdf 0.8+, a breaking change across arch_pdf.rs. So it is ignored with the reasoning written down AND with the condition that invalidates it: the moment anything in this crate reads a PDF -- an import feature, a thumbnailer, a preview pane -- this becomes a live DoS and the ignore must go. An ignore without its expiry condition is how real vulnerabilities get inherited. The other four advisories are unmaintained/unsound transitive crates (proc-macro-error, ttf-parser, atomic-polyfill, glib VariantStrIter), none with a safe upgrade, all arriving through the GUI/platform stack. Listed individually rather than disabling the unmaintained class, so a new one still fails. Six licence rejections were permissive licences simply absent from the allow list. MPL-2.0 (option-ext) is weak copyleft, so it is granted to that one crate rather than added globally -- MPL reciprocity is per-file and only bites if the crate is vendored and edited. The 126 git-source errors and 20 unlicensed crates are artifacts, not findings. Every git dependency is pinned to a full rev, which is stronger than a version range and already enforced by its own CI step; the unlicensed crates are first-party and vendored forks with no license field. Both handled WITHOUT allow-ing the class: sources are governed by the rev-pinning step, and the 20 crates are clarified by name, so a genuinely new unlicensed dependency still fails. Blanket-disabling would have hidden exactly the case worth catching. Separate file from deny.toml on purpose. Merging them would mean loosening the payment rules to fit a Makepad + Robius graph, and the payment policy is the one worth keeping tight. Verified both pay crates still pass unchanged. Negative-tested in both directions: removing the lopdf ignore fails the gate, and removing MIT from the allow list produces 79 rejections. A gate that cannot fail is worse than no gate. 622 lib + 154 integration, 0 failed. |
||
|
|
f20ba795c0 |
fix(cad): time-box script evaluation so a runaway cannot wedge the worker
Phase 2.4, which I deferred during Phase 2 on a wrong assumption.
An unterminated CAD script hung the evaluator permanently. vm.eval is a
single blocking call, so `while true { s = s.translate(..) }` never
returns: the rebuild worker blocks forever, every later edit queues behind
it, and the UI sits on "Computing 3D model..." until the app is killed.
The source is user- and AI-authored, and an LLM will emit a loop like that
without much provocation.
Reproduced before fixing. A probe test was still running at 60 seconds,
and again at 90 with the budget removed.
I deferred this originally saying it needed "a cooperative check inside
the Makepad script VM or a watchdog that can kill and respawn the worker
thread", and called it a VM design decision. That was wrong, and I should
have read the VM before concluding it. makepad_script already provides
ScriptRunBudget::from_durations(soft, hard, sample_interval), checked in
run_core against a sampled Instant. The CAD module simply never set it.
The fix is six lines in eval_cad_script_in_vm. PHASE2_STATUS.md now
records the bad call rather than quietly marking the item done.
Only the hard deadline is used. A soft hit sets TimeBudgetYield, which
expects a host that drives the VM back to completion; there is no such
driver here, so a soft deadline would present as a script that silently
produced nothing at all. Soft is set equal to hard so the budget can only
ever produce a real, reported error. The previous budget is saved and
restored, so one evaluation cannot starve the next.
5 seconds, sampled every 4096 instructions. Generous deliberately: a real
document is a few hundred CSG ops and finishes in milliseconds, so
anything still running is a runaway rather than a slow model.
Result: the same script now returns "script time budget exceeded" in 5.2s.
Three tests. The runaway case is #[ignore]d because it burns the whole
budget by design; the other two guard the ways a timeout typically breaks
things -- that a normal script is unaffected across repeated evaluations
(catching a budget that is not reset), and that a syntax error still
reports itself rather than being misattributed to the timeout.
CI gate added and negative-tested: losing the budget line reintroduces a
hang that no test failure announces, only a frozen app.
617 lib + 154 integration, 0 failed.
|
||
|
|
0405067bd5 |
fix(cad): properties panel and Extend tool never wrote anything (Phase 4.3)
Collapsing the nine duplicated properties-panel blocks exposed that all nine were silent no-ops, and so was the Extend tool. CadNode::pos/rot/size return Vec3f BY VALUE. `p.pos().x = val` therefore assigns to a temporary and discards it. It compiles, warns about nothing, and produces a control that does nothing: type a position into the panel and the part does not move. Confirmed with a standalone repro before touching anything. Eleven call sites, all wrong the same way: - the nine position/size/rotation inputs in workspace.rs - viewport.rs Extend, which carefully computes new_w/new_h and throws both away, so the tool has never extended a part Fixed by reading into a local, mutating it and calling set_pos/set_rot/ set_size, which have existed in cad_scene.rs the whole time. The duplication is what hid this. Nine copies of a 25-line block, each differing in one token, is not a thing anyone reads closely. Phase 4.3 was scheduled as tidying; it turned out to be the only reason the bug was found. The blocks now call one helper, apply_field_to_selected_part(cx, value, set), which also fixes the other hazard of the copy-paste: each block had to remember four separate invalidation steps (part_geoms.remove, invalidate_node, mark_scene_dirty, script_dirty), and missing the first one fails silently -- the part keeps rendering its old shape until something else evicts it. part_geoms.remove call sites in workspace.rs: 13 -> 5. Three defences, each negative-tested by reintroducing the defect: - cad_scene::size_tests::setters_write_through_but_getters_are_copies asserts both halves: that writing through the getter does NOT reach the node, and that the set_* methods do. It documents the trap rather than just guarding against it. - workspace::properties_panel_setter_tests applies all nine closures exactly as the call sites do and asserts each reaches its own axis and leaves the other two groups alone -- so a wrong-axis copy-paste fails too. Verified: restoring one broken closure fails with "size.y: setter did not reach the node (got 1)". - A CI grep for `.pos()/.rot()/.size().[xyz] =` outside comments. Verified it fires on the restored Extend defect. 610 lib + 154 integration, 0 failed. |
||
|
|
0a8d5b5abb |
test(cad): move the integration suite out of src/ (Phase 4.7)
src/.../cad/tests.rs -> tests/cad_integration.rs. It was 2,840 lines of integration tests living inside the library, compiled into every `--lib` build and able to reach anything in the crate. The move is worth more than the line count suggests, because a test target compiles as a separate crate and so sees only the public API. That turned an invisible question into a compile error: six items would have needed `pub` for the file to build in its new home — cad_mesh_data_from_solid, part_mesh_buffers, CommandBorrows, CadRenderMode, DrawingState, SnapSettings, plus the private fields of the last two. Visibility was not widened. Promoting editor internals to a public contract so a file can sit in a different directory is the wrong trade, and `pub` is far harder to take back than to grant. Instead the 16 tests that reach those items moved to where the items live: - viewport.rs gains `mod viewport_helper_tests` (10 tests: the mesh producers and the plain-data helpers). - mod.rs gains `mod editor_state_tests` (6 tests: SnapSettings polar defaults, DrawingState beam sections, CadEditorActivePane). Two of the relocated tests are worthless and are now visible as such: cad_render_mode_variants and command_borrows_fields_accessible assert that a type exists and derives Debug, which the compiler already guarantees. Left in place rather than deleted in the same commit as a move; they are for the Phase 6 sweep. The rule and its rationale are now written down in ARCHITECTURE.md under "Where a test goes", alongside corrected LOC figures for the six files that changed size since the table was written. CI gained a step. The existing job ran only `--lib`, which does not build a test target, so all 154 relocated tests would have run in no pipeline. The step names cad_integration explicitly instead of testing the whole crate, because tests/cost_estimator.rs and tests/cost_estimator_ui.rs do not compile — pre-existing upstream breakage, verified against a clean checkout, documented in a comment with instructions to fold them in once fixed. Test names diffed before and after: 0 lost, 12 gained (the Phase 4.4 characterization tests). 608 lib + 154 integration = 762. |
||
|
|
b5471e32e3 |
fix(cad): make the crate buildable, testable and safe to ship
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
PDF engine / engine (push) Has been cancelled
PDF engine / makepad-integration (push) Has been cancelled
PDF engine / fuzz (push) Has been cancelled
Phases 0-2 of CAD_ASSESSMENT_AND_PLAN.md. The crate did not compile and no
test had ever run; it now builds clean with a green suite.
Build and CI (Phase 0)
- Pin all 33 git dependency manifests to an explicit rev. A branch
dependency re-resolves on every build and is a code-execution path into
CI if force-pushed.
- Commit Cargo.lock (540 packages). Producing it required fixing three
resolution failures the workspace had always had: a non-existent
makepad-widgets feature, two rusqlite versions both linking sqlite3, and
four missed CellId call sites in spreadsheet-ui.
- Add .forgejo/workflows/nigig-build.yml.
- Replace five stale CAD docs that contradicted the code with one
ARCHITECTURE.md; add PHASE0/1/2_STATUS.md and TEST_BASELINE.md.
Correctness (Phase 1)
- Rotation units: transform_point bound sin_cos() backwards, transposed X
and Z, and applied axes in reverse order, so every exported STL was wrong
even at zero rotation. It now shares the renderer's matrix helpers.
- GLB quaternions had norm 0.125 (half-angle applied to cos/sin, degrees
read as radians) - invalid per the glTF spec.
- PDF wall/door/window yaw fed degrees to cos/sin.
- Fix a TOCTOU unwrap in touch picking; viewport.rs now has no unwrap().
- CommandContext gains update_node/insert_node_at/node_index: resize and
modify were delete+create, silently moving nodes to the end of the scene.
- Wire MAX_UNDO_LEVELS (defined, exported, never read) and switch the undo
stack to VecDeque; this also made the existing drag-merge logic reachable.
- CadNode::size() returned a fake 1x1x1 for CSG and extruded solids, making
them unpickable outside a 1x1x1 box at their origin.
- Reject non-finite script input; makepad_csg clamps NaN rather than
propagating it, so bad input produced silently wrong geometry.
Test baseline: 0 -> 722 passing, 0 failing
- 17 pre-existing failures fixed: 10 real defects (dependency-cycle
detection, over-allocation of unassigned tasks, quote/backslash
corruption on save, default rooms lost for all but the first region,
RGA text ordering) and 7 tests that were themselves wrong, each checked
against its production caller first.
Security (Phase 2)
- env!("CARGO_MANIFEST_DIR") was used as a runtime path in three places,
including as the AI agent's working directory. All runtime data now goes
under app_data_dir().
- Remove the hardcoded LAN LLM endpoint. It is now opt-in via
NIGIG_CAD_LOCAL_OPENAI_URL/_MODEL and refuses plaintext HTTP to anything
but loopback.
- Bound and content-sniff AI image attachments (8 MB cap, magic bytes);
the MIME type came from the filename extension.
- Escape SVG/HTML output, and add SRI to the exported viewer's script tag.
The pinned model-viewer@3.5.1 does not exist, so every exported viewer
was silently broken; now 4.0.0 with a verified hash.
- Stop embedding $USER in exported PDFs and logging document content in
release builds.
- CI now rejects reintroducing the runtime-path and hardcoded-endpoint
classes; both gates were verified to fail on a reintroduced defect.
Add system_prompt.md and embed it with include_str!. The file was missing
from the repository, so the agent silently used a one-line fallback.
|