Commit Graph
3954 Commits
Author SHA1 Message Date
ramox81andClaude Fable 5.1 b435af64cd Dynamic angular constraint: screen-size extension lines, reference grips, Properties parity
Dynamic dimensions carry DIMEXO/DIMEXE/DIMASZ overrides in text-height
ratios, a two-line angular dimension draws extension lines only beyond
its sides, a selected dynamic angle shows triangle grips at the arc ends
with squares at the arc point and text, and the constraint dimension's
Properties follow the reference: kind titles, General before Constraint,
angular-precision Value and Measurement, drawn text position, reference
row order for Fit, Primary Units and Tolerances, and text/dimension line
alignment rows reading On as the aligned state.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-22 11:58:59 +03:00
Karim Jerbi 874dd9debf perf(scene): optimize view render environment resolution and wire splitting
Eliminate major performance bottlenecks in the per-frame view render path:
- Memoize document render environment resolution using (geometry_epoch, len) keying, replacing per-viewport O(N) linear scans over document.objects.
- Index render_environments and render_settings handles in ObjectDataCache during the single build_cache pass.
- Cache resolved background image lookups in memory to eliminate repeated path.is_file() filesystem syscalls per frame.
- Add has_face3d fast-path to bypass wire splitting when no Face3D entities exist, and use FxHashSet<u64> handle indexing when present.
- Invalidate all cached structures across bump_geometry, bump_geometry_no_blocks, and bump_entities.
- Add comprehensive correctness unit tests and before/after benchmarks.

Performance impact (10k objects):
- P95 latency reduced from 341.00 µs to 173.00 µs (-49.3%)
- Throughput increased from 4,700 FPS to 6,000 FPS (+27.7%)
- Direct render environment evaluation: ~85 µs down to <50 ns (>1,700x)
2026-09-22 09:27:47 +01:00
ramox81andClaude Fable 5.1 160930b99a Angular dimension drawing: ray-based three-point frame, text beside the arc
Three-point angular dimensions take their sweep from the two rays and the
arc point (extension lines follow their own rays, straight and reflex
angles draw), dynamic angular dimensions store no text point so DIMTAD
places the text beside an unbroken arc, and dynamic dimensions carry a
DIMGAP override of a quarter text height.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-22 10:41:52 +03:00
ramox81andClaude Fable 5.1 4ad4041858 Angular dimensional constraint: reference flow, dynamic angular dimension, angN parameters
DCANGULAR picks two lines, an arc or three points, takes the dimension
line location to choose the sector and the value or name, and creates the
dynamic two-line or three-point angular dimension driven by an angN
parameter. Reference messages for invalid, repeated and parallel sides,
existing names and invalid names; angle values display within one turn at
the angular precision; the first side (or vertex and first point) stays
when the value changes; the ribbon Dimensional panel follows the reference
order.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-22 10:02:40 +03:00
yztink dad9c725c2 fix(viewcube): restore ViewCube visibility across display scales and window bounds 2026-09-21 23:39:48 -07:00
Alexey Bagno c0dccff14a fix(text): never accept a fallback glyph that has no outline
cosmic-text's fallback search can land on a face that claims the character and
outlines to nothing: macOS's system font does that for every glyph, because
ttf-parser reads no contours out of it. The code took the first non-.notdef
glyph id and stopped, so the character drew nothing while believing it drew
something — invisible text, and no warning either.

An empty outline is now treated as no answer: the search moves on to the next
face, and when cosmic-text's own list has nothing that draws the character, the
installed families are walked for one that does. That list is the platform's own
handful of names (on macOS `.SF NS`, `Menlo`, `Apple Color Emoji`, `Geneva`,
`Arial Unicode MS`) and covers neither Cyrillic nor Greek, however many fonts the
machine has that do.

`cargo test --lib a_fallback_glyph_is_never_empty`, and the Turkish letters in
`lff::tests::fonts_parse_and_resolve` render on macOS now instead of failing.
2026-09-22 09:39:43 +03:00
Alexey Bagno f954c7f495 fix(text): resolve a style's font file name against installed faces
A DWG text style stores its TrueType font as a *file* name — `GOST2304_TypeA_italic.ttf`,
sometimes with a directory — which is what AutoCAD resolves. The family lives
inside the file and is usually a different string (`GOST2304 Type A`), so looking
the reference up as a family missed an installed font and fell through to the
embedded stroke fonts. Those carry no Cyrillic at all (`simplex`, `standard`,
`romans`, `iso3098` hold no U+04xx glyph), so a Russian drawing came out with
every word missing: the letters went on to the system-font fallback, which
returns nothing on macOS.

Installed faces are now indexed by file name, and a reference — a style's font
file, or an inline `\f` code, which carries either form — resolves by family
first and by file name second. An embedded stroke font still wins, so
`romans.shx` keeps rendering as it did.

`cargo test --lib face_files_resolve_to_their_family`
2026-09-22 09:39:43 +03:00
yztink 33dfbf9230 fix(command): support direct distance entry when dynamic input is disabled 2026-09-21 23:00:04 -07:00
yztink 270b6102de fix(plugin): drive headless tasks, invalidate layer cache, and handle cancel event 2026-09-21 16:29:01 -07:00
Karim Jerbi 960d51d955 fix(boundary): clip rays/XLINEs to finite extents for face detection
Infinite construction lines tessellated to a single unit stub at the base
point, which can never close a face (and overwrote the real wire segments),
so XLINEs were silently ignored by BOUNDARY. Clip unbounded curves to the
finite extents via Liang-Barsky; lines missing the geometry contribute
nothing. Same shared helper fixes RAYs.

Tests:
- xline_splits_rectangle_into_two_faces: 10x10 rect split in two
- ray_splits_rectangle_into_two_faces: upward ray splits in two
- xline_missing_the_geometry_contributes_no_face: distant xline is a no-op
2026-09-21 23:30:57 +01:00
Karim Jerbi 4432f633e7 perf(glyphs): memoize constraint-glyph placements behind constraints_epoch
Placements (entity lookups, intersections, String labels, projections)
recomputed per view() and per mouse-move hit-test, with overlay re-clones
and 2-3x offset recomputes. New constraints_epoch bumped on every set
mutation/visibility/undo/DWG-load (plus new set_constraint_enabled API);
single-slot Arc<[GlyphEntry]> memo on an 11-field key (hover/selection
excluded); overlay de-cloned/de-duped to the single geometry source;
hit paths reuse the cache.

Tests:
- cached_glyph_placements_hit_returns_same_arc: same-key Arc identity
- cached_glyph_placements_invalidated_by_metadata_edit: edit + selection change yield new Arc
- glyph_hit_test_entries_matches_precomputed_boxes: shared hit-test helper

Bench (cargo bench glyph, 60 constraints): 8.18us -> 0.05-0.15us (~55x).
A/B reference deleted.
2026-09-21 22:52:47 +01:00
Karim Jerbi 2d49204c50 perf(grips): cap total selection grips at 4096
GRIPOBJLIMIT gates object count, but one dense polyline emits ~2
grips/vertex past it (~200K grips through the per-frame project + draw
path, ms-scale, no cache). Enforce a vertex budget once in
refresh_selected_grips: stable vertex-first priority, entity order
restored, handles truncated in lockstep. Below the cap selections are
byte-identical.

Tests:
- refresh_selected_grips_caps_total_grip_count: cap + handles lockstep
- refresh_selected_grips_prefers_vertex_grips_over_midpoints: vertices survive
- grip_budget_boundary_lengths: empty/cap/cap+1 edge cases

Bench (cargo bench grip, 200K-grip fixture): per-frame 6.8ms -> 0.13ms
(45-52x); select-time build 2.36ms full. A/B reference deleted.
2026-09-21 22:52:41 +01:00
Karim Jerbi dcc05f310d perf(statusbar): cache layout/scale derived data behind epochs (major)
Status bar rescanned the whole object map twice per frame (layout_names +
scale_picker_list), deep-cloned an already-cached Arc, and built two
undo/redo label vecs consumed ~never. Cache layout names (epoch key) and
scale picker (epoch + unit-factor + imperial-family + current-scale key)
as Arc behind choke-point invalidation (layout/scale impl methods,
undo/redo swaps, file-open, doc-reset); borrow across the widget boundary
(owned-Arc params; E0515 forbids &'a slices over call-site temporaries);
build undo/redo labels only when their dropdown is open.

Tests:
- cached_layout_names_returns_same_arc_until_bump: hit-path Arc identity
- cached_layout_names_invalidated_by_add_layout: bump yields new Arc + names
- cached_scale_picker_returns_same_arc_until_bump: hit-path Arc identity
- cached_scale_picker_invalidated_by_add_scale: bump yields new Arc + scale
- cached_scale_picker_invalidated_by_current_scale_change: key completeness

Bench (cargo bench ui_statusbar_derived_data, 3k entities): cached hit below
timer granularity vs ~1us uncached scans, PASS (50us threshold).
2026-09-21 22:31:16 +01:00
Alexey Bagno 279d77a950 docs(trackpad): document the gestures and why a pinch bypasses iced
The pinch is read from AppKit rather than through iced, which is the kind of
decision that reads as arbitrary without the reason next to it. The file also
records what the pixel/notch split costs: a smooth-scrolling mouse pans with a
trackpad.
2026-09-21 23:47:32 +03:00
Alexey Bagno 13b6a08e77 feat(viewport): two-finger trackpad pan and pinch zoom
A trackpad could only zoom: every scroll delta, wheel notch or not, went
into the same zoom step, and a pinch never arrived at all. iced's winit
layer converts only the window events it has an iced equivalent for, so
winit's `PinchGesture` was dropped before the app saw it.

Scroll deltas now split by what produced them: a wheel reports notches and
still zooms, a precise-scrolling device reports pixels and pans. The same
pixel split keeps working everywhere else — the browser build reports every
wheel notch as pixels, so there they keep zooming.

The pinch is read straight from AppKit with a local `NSEvent` monitor, since
the app cannot see the gesture through iced at all, and handed to the app
through a subscription shaped like the SpaceMouse bridge. It maps 1:1 onto
the camera: fingers 5% further apart leave the view 5% closer.

The pan and zoom bodies are now shared by the mouse and the gestures, so the
middle-button drag, the wheel, two fingers and a pinch all move the view
through one path — in model space, in a tiled pane and inside a floating
viewport alike.
2026-09-21 23:47:32 +03:00
Felix Riestra fb1d1116ce docs: commit count in the guide 2026-09-21 22:36:34 +02:00
Felix Riestra 36f35da570 docs: final size figures 2026-09-21 22:36:24 +02:00
Felix RiestraandClaude Sonnet 5 5946ee6934 chore: untrack docs/.DS_Store
Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 22:35:58 +02:00
Felix RiestraandClaude Sonnet 5 2489dda5b9 chore: drop stray .DS_Store files; correct the release workflow note in the guide
Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 22:35:39 +02:00
Felix Riestra b8ccd3cd5c docs: update the change size 2026-09-21 22:35:19 +02:00
Felix RiestraandClaude Sonnet 5 4e1a78d3ab ci: leave the fork-only build workflow out of the pull request
Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 22:35:09 +02:00
Felix RiestraandClaude Sonnet 5 8f68006bea docs: make the change ready for upstream review
Remove personal names, local paths and fork-only process notes from code comments,
docs and the plugin manifest (repository now points at the upstream project), and
drop the handoff section from the plan.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 22:35:09 +02:00
Felix RiestraandClaude Sonnet 5 c7c698eaad docs: refresh the review guide and plan after merging upstream main
Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 22:27:43 +02:00
Felix Riestra b1e6d2a349 Merge remote-tracking branch 'origin/main' into plugin/host-model-api 2026-09-21 22:20:09 +02:00
Felix RiestraandClaude Sonnet 5 41d3613b85 docs: reviewer guide for the scripting host model and Python plugin
What it is, how to check it on Linux or Windows without a Mac, the reading order,
the small edits to existing code, what each area proves, the decisions that are the
maintainer's, and what is deliberately not done.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 22:16:06 +02:00
Felix RiestraandClaude Sonnet 5 f3873eff4b docs: record the pull-request check workflow and its first run
Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 22:12:09 +02:00
Hakan SevenandClaude Fable 5.1 baba5bccd3 build: reach lopdf and encoding_rs through printpdf and the codec
#1418 added both as direct dependencies although each was already in the
tree: printpdf's to_lopdf_doc returns the lopdf document whose compress
and save_to are plain methods, and the codec's encoding_from_code_page
hands back the Big5 / GBK / Shift_JIS / EUC-KR encodings by their DXF
code-page names. Cargo.toml keeps its dependency list.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 22:55:04 +03:00
Hakan Seven 697ea4984b Merge pull request #1418 from Chrisyeh1983/fix/viewport-wide-polyline-plot-width
fix: wide-polyline plot width through viewports; CJK text (big fonts, fallback size, atlas growth, MTEXT wrap/indents); compressed PDF
2026-09-21 22:49:31 +03:00
Hakan SevenandClaude Fable 5.1 4f12087539 test(constraints): a negative distance value validates by its magnitude
#1415 made a dimensional Distance keep a negative value's sign in the
parameter while the geometry uses its magnitude (and a zero fold the
points together), but left the value-domain test asserting the old
refusal. Assert the new reading, and keep the radius refusal covered.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 22:49:31 +03:00
Hakan Seven 7490b2bebd Merge pull request #1415 from ramox81/codex/aligned-constraint-parity
Aligned dimensional constraint: Point & line, 2Lines, value and property parity

# Conflicts:
#	src/app/command_driver.rs
#	src/app/update/viewport.rs
#	src/entities/dimension.rs
#	src/scene/parametric_constraints.rs
2026-09-21 22:41:42 +03:00
Felix RiestraandClaude Sonnet 5 1f8cbb8128 ci: Python host check for pull requests on Linux and Windows
Runs the plugin API tests, the plugin crate and Python model tests, and the host's
real-IPC plugin tests against a freshly staged plugin and built runner, and fails
if either environment variable target is missing so the tests cannot skip silently.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 21:40:46 +02:00
Hakan Seven 0ee8b15511 Merge pull request #1413 from ramox81/codex/linear-constraint-parity
Linear dimensional constraints: point flow, dynamic dimension, driving parameter, persistence

# Conflicts:
#	src/scene/parametric_constraints.rs
2026-09-21 22:36:20 +03:00
Hakan Seven 2208be9506 Merge pull request #1409 from ramox81/codex/equal-followups
Equal follow-ups: one glyph per object, unshown Multiple bars, typed object picks
2026-09-21 22:35:00 +03:00
Hakan SevenandClaude Fable 5.1 e537a495c6 fix(constraints): point symmetry converges from any start
Symmetric held the connecting direction perpendicular to the axis with
the kernel's angle-based Perpendicular, which locks its turn side at
construction: a point starting far from its mirror image had to swing
the pair through the axis direction, the residual wrapped, and the
solver stalled with the point unmoved. The perpendicularity is now the
projection of the pair onto the axis direction being zero, a polynomial
residual the solver follows from anywhere. Circle centers and ellipse
centers use the same equation. tests/parametric_constraints_solve.rs
symmetric_constraint_mirrors_one_circles_center_across_the_axis_line
passes again.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 22:21:19 +03:00
Hakan SevenandClaude Fable 5.1 ececb0ef8c ci: run the workspace tests on every change; drop the unused flake
- .github/workflows/ci.yml runs `cargo test --workspace --locked` on
  pushes to main and on pull requests, with the Linux libraries the
  release build installs. No formatter check: the tree is not
  rustfmt-clean and stays that way.
- flake.nix, flake.lock and the direnv hook are removed. Nothing used
  them, and their pinned dependency hashes were already stale after the
  codec and kernel bumps.
- Under cfg(test) the "system" language resolves to en-US. The unit
  tests compare English text through one process-wide loader, and every
  apply_config of a default config asked for the system language, so a
  Turkish locale failed 22 tests and parallel tests raced each other
  through a language switch (the thickness-row test found its section
  under one language and looked it up under another).
- The arc grip test follows #1352: the start, end and midpoint grips
  drive the whole arc.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 22:21:19 +03:00
Felix RiestraandClaude Sonnet 5 899ec19285 docs: record the Linux and Windows fork build results
Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 20:48:58 +02:00
Felix RiestraandClaude Sonnet 5 20f75c34e1 test: pass forward-slash temp paths to generated Python scripts
On Windows the temp directory has backslashes, which a Python string literal reads
as escape sequences (C:\Users is a unicode escape), so the RasterImage and OLE
lifecycle tests could not even create their entity. Windows accepts forward slashes.
Found by the first Windows run of the fork build workflow; Linux passed all 60.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 19:56:22 +02:00
Felix RiestraandClaude Sonnet 5 33dfb21cfb ci: fork build workflow for Linux and Windows with the bundled Python plugin
Portable builds (executable plus plugins/opencad.python where the host looks for
it) and the real-IPC plugin tests on ubuntu and windows. Started by pushing a
fork-build-* tag or by hand; publishes nothing. release.yml stages the plugin
only in the macOS bundle.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 19:01:15 +02:00
Felix RiestraandClaude Sonnet 5 d2c2ef178b docs: record cadcodec PR #51 for the text and dimension style DXF fixes
Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 18:55:42 +02:00
Felix RiestraandClaude Sonnet 5 2ed41586de feat: SCRIPTCOMMANDS setting to turn script-driven commands off
A saved user preference (on by default) toggled by the SCRIPTCOMMANDS command
gates HostApi::run_command; Cancel stays allowed so a waiting command is never
stranded. The runner refuses the SCRIPTCOMMANDS command itself, so a script
cannot change it. Documents why no helper-thread or stack-growing dependency was
added: native builds link with at least 8 MiB, the nesting depth is fixed, and the
deepest step found needs 2 to 4 MiB in a debug build.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 18:48:27 +02:00
Felix RiestraandClaude Sonnet 5 a25ae4f8c0 chore: clear compiler warnings introduced by the Python host work
Drop two unneeded mut bindings in tests and mark the generated dict/enum
converters allow(dead_code), since a kind can leave one unused.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 18:31:23 +02:00
Felix RiestraandClaude Sonnet 5 8eb85dab8d docs: final review pass over the Python host documentation
Rewrite the host-model design note for the current architecture (entities, tables,
commands, nested validation, known limits), refresh the plan's current-state header,
the ledger and gaps-doc status lines (cadcodec PR #48), the README status and the
superseded ocs.command notes, and drop dangling references to a sibling project that
is not in this repository.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 18:27:59 +02:00
Felix RiestraandClaude Sonnet 5 b180dce1a1 feat(python): PEDIT polyline wrappers and path/3-D arrays
polyline_close/open/width/reverse/join (a line or arc is turned into a polyline
first), array_path and array_3d, written against observed prompts and checked
for exact geometry in the real-IPC audit. Polylines default their pick point to
the first vertex. The audit runs on a large-stack thread because PEDIT's
line-to-polyline step needs more than a test thread's 2 MiB in a debug build.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 17:57:58 +02:00
Felix RiestraandClaude Sonnet 5 81251d9cf3 fix(python): validate nested input (vertices, edges) instead of defaulting silently
The generated dict-to-struct conversions for nested records defaulted every
absent field and ignored unknown keys, so an LwPolyline built from
{'x': .., 'y': ..} vertices became zero-coordinate vertices. They now refuse an
unknown key (naming the keys the record takes) and a manifest list,
required_struct_fields, enforces the fields that define a nested record's
geometry (vertex locations, hatch edge ends and centres, section symbol points).
Regression test over real IPC; every existing lifecycle audit still passes.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 17:42:35 +02:00
Felix RiestraandClaude Sonnet 5 360dd853ed feat(python): chamfer, arrays, explode, join, break, stretch and lengthen wrappers
Each is written against the prompt sequence observed in the exploration harness
and checked for exact geometry in the real-IPC audit. HATCH is left to
create_entity because its command needs an interactive boundary pick.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 17:34:18 +02:00
Felix RiestraandClaude Sonnet 5 1b1785eefb feat(python): command runner and modify wrappers (doc.command, doc.modify)
HostApi::run_command drives the real OCS command through the same primitives
the automation channel uses, synchronously on the host thread: run a line,
start a command and answer point/text/token/entity/selection/enter prompts, or
cancel (closing anything the command opened). Refused while another command is
active, for a denylist (QUIT, NEW, OPEN, SAVE*, PY_* ...) and off the active tab.
Python: doc.command, doc.start_command and doc.modify (offset, trim, extend,
fillet, move, copy, rotate, scale, mirror, erase). Real-IPC audit checks exact
geometry for every wrapper plus guards and undo; IPC and Python unit tests; docs.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 17:25:05 +02:00
Felix RiestraandClaude Sonnet 5 795aa922b4 feat(python): linetypes and layouts (doc.linetypes, doc.layouts)
TableOperation gains simple-linetype create/modify/rename/delete (standard names
taken, rename follows layers and entities, guarded delete) and paper-space
layout create/rename/delete/set-current/set-page (default page setup and sheet
viewport, the application's own layout switch, Model protected). CTAB now reports
the current layout. Real-IPC audit with DWG/DXF persistence and undo, IPC and
Python unit tests, docs.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 17:05:48 +02:00
Felix RiestraandClaude Sonnet 5 b1edc9f578 feat(python): block contents (create_entity(..., block=))
BlockEntityAdd adds a validated entity to a block definition (owner set by the
host, same validation as a model-space entity, self-nesting inserts refused),
reusing the scene's block-editor routing. Deleting a block member now also
drops its handle from the block record. Real-IPC audit with an attribute
definition inside a block, DWG/DXF persistence and undo; IPC and Python unit
tests; docs.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 16:54:30 +02:00
Felix RiestraandClaude Sonnet 5 8d20d280fe feat(python): block definitions (doc.blocks)
TableOperation gains BlockCreate (copy entities into a definition shifted by the
base point, optionally erasing originals), BlockModify, BlockRename (inserts
follow) and BlockDelete (refused while referenced; removes members, markers and
orphaned draw-order tables). Real-IPC audit with DWG/DXF persistence and undo,
IPC and Python unit tests, docs; the DXF loss of the block description is pinned
by a canary.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 16:28:03 +02:00
Felix RiestraandClaude Sonnet 5 aaea15e4ef feat(python): text and dimension styles (doc.text_styles, doc.dim_styles)
Extends TableOperation with text style create/modify, dimension style
create/modify (DimStyle fields as a validated JSON object), rename, delete and
set-current. Reuses OCS's style in-use scan and reference-following rename
(four style_ops methods widened to pub(super)); a renamed text style also
updates dimension styles that name it. Real-IPC audit with DWG/DXF persistence
and undo, IPC and Python unit tests, docs. Four cadcodec findings are pinned by
canaries.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 16:18:22 +02:00