Repository navigation
carmenta: speed (4-6x), deskew, word boxes, cheap auto_orient, table order: a downstream caller's 13 findings - #55
Merged
Conversation
…5x on PdfCraft's pages PdfCraft (artcraft-15) measured mobiledet-svtr at 6.7-7.3x slower than ocrs, recognition ~300 ms a line. The cause was candle's grouped convolution: it runs a depthwise layer as one im2col convolution PER CHANNEL (candle-core 0.11 conv.rs:262), and SVTR has 14 of them per line, MobileDet 14 per page. - dwconv: a direct depthwise stencil, per-axis stride and padding, parallel over planes. On AVX2+FMA it accumulates with fused multiply-adds in candle's (ky, kx) order and is BIT-IDENTICAL to candle's grouped path (unit test on every SVTR and MobileDet shape). FFAI_DWCONV=0 restores candle. - SVTR: each backbone block's bias + LAB + hardswish + LAB (eight candle passes) is one pass with candle's exact arithmetic; 1x1 convs are a plain NCHW matmul; the (2,2) stem uses candle's native stride. FFAI_SVTR_FUSED=0 restores the old chain. The forward now reports rec_cnn / rec_rnn / rec_head on FFAI_PROFILE. - MobileDet: the same epilogue fusion and 1x1 matmul, 3x3 neck and head convs through conv3x3's register-tiled kernel, and the two k=s=2 transposed convs as a matmul plus interleave. FFAI_MDET_FAST=0 restores candle. Gates: - SVTR probabilities on 30 real line crops (5 pages): bit-identical to the candle path, so the paddle oracle's 8.857e-5 carries over unchanged. - MobileDet paddle oracle: max |dprob| 8.294e-5 -> 7.537e-5, 0/65536 binarised disagreement, boxes 4/4. - PdfCraft's 42-page corpus: identical text and line boxes on every page. Speed (examples/pdfcraft_bench, i7-14650HX): - 42 pages, 24 threads: 175.2 s -> 40.3 s (4.35x). - one 300 dpi page, 1 thread: 6.07 s -> 3.54 s; det 2.24 -> 0.92 s, rec 164 -> 109 ms a line. Also: the embedded-manifest sync test compares with line endings normalised; a core.autocrlf checkout can hold the two copies with different endings while git considers both clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ement at the line fan-out Built and measured after the depthwise work: a thread-local serial flag for inner ops plus direct gemm calls with Parallelism::None inside the fan-out (output bit-identical). Slower: 1.066x at 24 threads (0/6 ABBA wins), 1.040x at 8 (1/6). Serialising the lines is worse (55.6 s vs 40.3 s). Reverted; the comment keeps the numbers so it is not rebuilt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
….29% -> 0.10% CER PdfCraft measured an axis-aligned crop of a tilted line reading the lines above and below it: 0.41% CER at 0.5 degrees, 93.3% at 2, 98.8% at 5, and 61% on a phone photo tilted 1.5 degrees. Its own deskew pass fixed that; this puts the same fix in the engine, for every document caller. - mobiledet::skew_degrees: the page angle from the detector's own regions -- the min-area rectangles the DB postprocess already fits -- as the length-weighted median direction of the line-shaped ones, reported only when 60% of the weight agrees with it to a degree. Measured in the normal detection pass, so a square page costs nothing extra. - A page at least 0.3 degrees off is rotated back once (bilinear, onto a canvas holding every corner, filled with the page's paper colour), read, and its boxes mapped back onto the caller's image. - ffai-core: OcrOptions::deskew (opt-in, like auto_orient; engines that cannot honour it error) and OcrOutput::skew_degrees. deskew::rotated_corners recovers a line's turned rectangle from its box and the angle, for a text layer drawn along the page's slope. PdfCraft's 42-page corpus: skew 0.5/1/2/5 group 48.29% -> 0.10% (PdfCraft's own pass: 0.14%), photo 61.00% -> 0.11% (0.11%). The other 37 pages read byte-identical text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s; ffai-models off dirs PdfCraft (artcraft-15) measured an OCR-only consumer going from 67 to 185 crates and from 172 s to 312 s for a clean build, and cargo-deny red under a permissive-only licence policy. - ffai-carmenta listed candle-transformers and tokenizers (native: with Oniguruma, a C library; wasm: with unstable_wasm and getrandom for it). Nothing in the crate used either. Removed; native, all-targets and wasm32 builds check. candle-core 0.11 still pulls tokenizers + onig on native targets itself; that needs candle to make it optional. - ffai-models used `dirs` for one call, the cache directory. Its dirs-sys pulls option-ext (MPL-2.0). Replaced with the same platform rules written against the environment. With `fetch` off (the offline path) option-ext is gone from the tree; with `fetch` on, hf-hub's xet stack still brings dirs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d the ink OcrLine::words was always empty for the line recognizers, so a caller drawing a word-level text layer re-split each line itself. PdfCraft's attempt with split_ink_words matched the word count on 38 of 472 lines; its own splitter boxed 92% of words. The recognizer already knows. ctc_greedy_emits records the frames of every emission (text and confidence byte-identical to ctc_greedy / _masked: the same per-frame choice, the same loop), the decoded spaces group characters into words, and words.rs places them: - the cut between two words is the widest ink-free column run between the last frame of one and the first frame of the next; - each word's box is its ink inside its cuts: the gap-bridged band of ink rows carrying the most ink (not the densest row: a DB line box reaches into the neighbouring lines, ~2.5x the ink height), then its first and last ink columns. Ink is measured against the crop's own paper level, so inverted and low-contrast lines work. Word texts are the line text split at its spaces (with the same fold), so they join back into it. Word boxes follow auto_orient and deskew back into the caller's coordinates; the formula router clears them when it rewrites a line's text. PdfCraft's corpus, deskew on: word boxes (same text, IoU >= 0.5 against ground-truth ink boxes) 1% -> 96% overall; clean 100%, fonts 100% (PdfCraft 79%), money 99% (88%), small 98% (92%), table 100%, accents 83%. CER unchanged on every page group. 574 of 575 lines carry words. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n unconvincing PdfCraft measured auto_orient at 2.29x the time of an upright 300 dpi page on one core (18.3 -> 43.6 s) and 2.17x on eight: the decision (a detection pass plus two dozen crop reads) ran on every page, and an upright page was then read in full anyway. Its corpus also showed the page's own confidence separating the cases: upright 0.967-0.997 (0.806 for all capitals), turned 90/180/270 0.000 / 0.474 / 0.267, blank none. Now the page is read as given; a read at confidence >= 0.7 (characters- weighted mean of its lines) is returned as is, and only otherwise does the decision run (returning the first read when it says upright). PdfCraft's corpus, deskew on: upright pages identical output, time 1.004x (ABBA over 10 pages, 3/6 wins); rotated 90/180/270 91.27% -> 0.00% CER; ALL 6.23% -> 0.90%. README: orientation row corrected (the 48% figure was the decision alone), deskew and word-box rows added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cess affinity mask std::thread::available_parallelism ignores the affinity mask on Windows (24 under `start /affinity 4`), so rayon's global pool and candle's gemm fan-out ran two dozen threads on a process pinned to one core. PdfCraft measured sizing the pool to the mask (ABBA, identical output): 8 cores 0.927x time (10/10) and peak 1959 -> 1240 MB; 1 core neutral time and 1285 -> 747 MB. It could not read the mask itself: that is an FFI call, and it forbids unsafe. available_threads() is that call (GetProcessAffinityMask, one SAFETY-noted unsafe line) behind a safe function, falling back to available_parallelism elsewhere; affinity_pool() builds a rayon pool of that size to install the engine in. windows-sys 0.61 was already in the tree. Checked: 1 under mask 0x4, 8 under 0x3FC, 24 unmasked. pdfcraft_bench reports it and takes BENCH_POOL=1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lowercase French/German/Spanish read 1.9 / 0.9 / 0.1 % CER on PdfCraft's rendered pages, but the 18 383-character head cannot emit À Â Ê Ë Ì Î Ï Ñ Ò Ô Õ Ø Ù Ú Û Ý, î ï ÿ œ or « » ¿ ¡ ‘ ™ ‰ (checked against charset.txt): a page of accented capitals read 24 % CER. OcrOptions::charset is the documented way to keep such a page from emitting CJK look-alikes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…collapsible if) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…> 0% CER) The recursive cut read PdfCraft's 13-row ruled table as columns -- its gutters are the widest gaps on the page -- so every word came back right in an order scoring 77% CER (ocrs, reading rows: 7%). boxes::grid_order runs on the FINISHED line order (after the cut, the sparse gate and the 8.157 probe reorder, any of which scattered rows re-ordered earlier) and re-emits a table band row-major into the slots its lines held, so nothing outside a band moves. A band is 4+ consecutive rows that are all: - grid rows: 2+ cells, every gap >= the wider neighbour (1.5x for 2 cells); - columns: the same cell count, cell j overlapping the previous row's cell j; - short: median cell width/height <= 8. FFAI_ORDER_GRID=0 disables. Gated on OmniDocBench (315 pages, one binary, FFAI_ORDER_GRID=0 vs on). The first two versions were REFUTED there: gap + alignment alone changed 29 and then 13 pages, 12 worse (omni-0137 3.07% -> 14.15%): three-column newspapers whose justified lines split into fragments, and two prose columns either side of a photograph, which pass every geometric test except cell shape. With the short-cell condition: 1 page changed, better (omni-0005 83.47 -> 83.37%), macro 22.682% -> 22.681%. PdfCraft's corpus: table-300 77.04% -> 0.00%, no other page changed. README: OmniDocBench pages now read at 1.4 s median / 2.7 s p90 per page on 24 threads (was measured at ~9-17 s/page). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uted per-page-type cap #13 (PdfCraft): its 344x108 "HELLO WORLD" test image (28 px blocky glyphs) read `HELL` + `WORLO`. The 736 px floor enlarged it 6.8x, the box scores sank to 0.41-0.64 against the 0.6 threshold, and the final O fell outside its box. The floor exists for small TEXT, not small images: capping its factor at 4x (FFAI_DET_MAX_UP, 0 = uncapped) gives one box at 0.91 and `HELLO`. The `D` -> `O` that remains is the recognizer's own reading of a blocky 5x7 D (it differs from the O by two corner dots), not geometry. Inert elsewhere: carmenta-form's 240 single-line clips (4.9x before) identical, 0.407% CER; carmenta-capture (3.68x) below the cap; PdfCraft's 42 pages byte-identical. #11 (PdfCraft): a lower detector cap for rendered pages. Measured 1280 vs 736 and refuted, now documented at DET_MAX_SHORT_DEFAULT: content::classify calls all 32 carmenta-doc scans Rendered, and 736 loses on 14 of them (up to +18.4 pp); on rendered pages the ruled table goes 0 -> 32.7% and capitals 24.1 -> 31.0%; OmniDocBench is a wash (65 worse, 67 better); a median-text-height signal does not separate the losses (9.1-11.9 px vs neutral at 8.7 px). 1280 stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… nothing) PdfCraft finding #12: the ppocrv5 manifests pin hashes but name no hf_repo, so no crates.io user can fetch the document engine's weights. ffai-models already downloads by file name from a manifest's hf_repo; the repo itself is missing, and publishing is the owner's call. This copies exactly the files the engine loads from the cache, verifies each against the embedded manifest, writes a model card with the PaddleOCR Apache-2.0 attribution and a LICENSE, and prints the two upload commands and the hf_repo line to add. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a local one; documents BENCH_POOL Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…wrecks every corpus) FFAI_MDET_PAD_Y=0.3 read the blocky 5x7 D correctly and took PdfCraft's 42 pages from 0.25% to 9.93% CER (ruled table 0 -> 55%), carmenta-form 0.41 -> 2.53% (73/240 worse), carmenta-doc 30.1 -> 48.5% (32/32 worse). Default stays 0; the 5x7 D -> O is recorded as a known misread of a blocky font. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pocrv5 manifests PdfCraft finding #12: the document engine's weights had pinned hashes and no host, so no crates.io user could fetch them. They are now public at huggingface.co/remade-with-rust/carmenta-ppocrv5-mobile (first commit 637f0fc): det-fused.safetensors, rec.safetensors and charset.txt, with a model card and the PaddleOCR Apache-2.0 notice. Each file was downloaded back anonymously and matches the sha256 its manifest pins. ppocrv5-mobile-det and -rec now name the repo (crate and repository copies, identical), so `fetch` downloads by file name and verifies as it already does; `offline` users can download the same three files. The staging tool's default repo is the lowercase organisation name; the README gains the host. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion already did The first download from the published PP-OCRv5 repo failed: the cache path called ModelManifest::fetch(), which resolves EVERY declared file, and the detector manifest also lists det.safetensors and det-train.safetensors for provenance -- files no engine opens and the repo does not carry. Offline resolution already asked only for the needed files (resolve_only_in). ModelManifest::fetch_only(needed) is the download-path twin, with the same checksum verification and the same refusal of undeclared names; fetch() is fetch_only(all). Carmenta's resolver uses it for every source. Checked end to end: an empty FFAI_CACHE/HF_HOME, `ocr_text mobiledet-svtr` downloaded exactly det-fused.safetensors, rec.safetensors and charset.txt from remade-with-rust/carmenta-ppocrv5-mobile @ 637f0fc, verified them and read the page correctly. Unit test for the undeclared-name refusal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ld quietly The crates set `unsafe_code = "warn"`, and a crate pulled in by path or git rev is not lint-capped the way a registry crate is: PdfCraft saw 18 warnings from its pinned build. Each site now carries its own allow, as threads.rs and fastops.rs already did: the SIMD kernels and their dispatch arms in fastmath, conv3x3 and dwconv, the Win32 trim in release_load_arena, and the two mmapped safetensors loads. Allows sit on fns, statements and lets, never on a tail expression. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…corners takes it Boxes come back in the input's coordinates, so a page read at 90 and one read at 270 degrees gave the same boxes and the reading direction was lost. `OcrOutput::turns` (0-3 clockwise quarter turns) now says which, set when the turned read is kept. With auto_orient and deskew both on, `skew_degrees` is measured on the upright page while the boxes are mapped back to the input, so `rotated_corners` met a tall box with a near-horizontal angle and clamped the line's width to 0. It now takes `turns`: on the input the rectangle runs at `degrees - 90 * turns`, which the bounds solve handles as it is. Tested for turns 1-3 at 2 degrees against the exact point map. auto_orient also compares the two reads now: the turned one is kept only when its page confidence beats the first, so a low-confidence upright page the sampled decision misjudged keeps its own read. PdfCraft's 42 pages read byte-identically (text and boxes) with orient and deskew on. Findings 1-3 from PdfCraft's rerun. BREAKING CHANGE: `OcrOutput` gains `turns` and, with the unreleased `skew_degrees` and `OcrOptions::deskew`, breaks struct literals and exhaustive destructuring downstream; `deskew::rotated_corners` takes a third argument. Not `#[non_exhaustive]`: that would forbid the `..Default::default()` construction callers use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
route.rs cleared a line's words whenever an inline formula was spliced into its text, which dropped every box on the line for one token. Now the words wholly outside the splice keep theirs, and the words it touches become one word holding the spliced token, boxed by their union. Words that cannot be found in the text in order are still cleared. It happened on 0 of 1,019 lines in PdfCraft's corpus, but form routing can reach it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A harness that pins the process after spawning it (pinvs does) can lose a race against the top of main, and BENCH_POOL=1 then built a pool for the whole machine. The mask is read after preload(), and the work row prints the pool the pages actually ran on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1.0.11; dated zip ignores cargo-deny started failing on master and here on nine new advisories. Six have semver-compatible fixes and are taken in the lockfile (RUSTSEC-2026-0346, -0347, -0348, -0349 in rustls; -0382 in rustls-webpki; -0383 in webpki-root-certs). The other three (RUSTSEC-2026-0367, -0368, -0369) are zip 8.6 extraction bugs fixed only in zip 9, which candle-core 0.11 cannot take (it pins ^8.6). They live in ZipArchive::extract / ZipStreamReader::extract; candle opens archives and reads entries by name and never extracts, and nothing here depends on zip directly. Ignored with a dated, scoped justification and a 2026-11-15 review, per deny.toml's policy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ndent past crates.io ffai-core's OcrOptions/OcrOutput gained fields (deskew, skew_degrees, turns), a breaking change, so each published crate that depends on it gets a new version requiring core 0.9. Republishing only core and carmenta would leave a caller with, for example, ffai-media built on core 0.8 beside carmenta on core 0.9: two ImageBuffer types that do not unify. ffai-core 0.9.0, ffai-carmenta 0.13.0, ffai-models 0.7.0, ffai-media 0.7.0, ffai-mercury 1.1.0, ffai-diana 0.9.0, ffai-argus 0.9.0 (workspace.package, with ffai-demo), ffai-bench 0.8.0, ffai-cli 0.9.0, ffai-argus-wasm 0.3.0, ffai-carmenta-wasm 0.4.0, ffai-mercury-wasm 0.3.0, ffai-wasm 0.3.0 Each crate's CHANGELOG has the entry, and the install snippets in the argus, carmenta, mercury, media and diana READMEs name the new versions (mercury's still said 0.7). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ies the zip ignores cargo-audit scans more of the tree than cargo-deny and reported six more: RUSTSEC-2026-0338 to -0342 in quinn-proto and -0381 in webpki-roots, all with semver-compatible fixes, taken in the lockfile. It also has its own ignore list, which did not exist, so it still failed on the three zip 8.6 extraction advisories deny.toml accepts (RUSTSEC-2026-0367/0368/0369; candle-core never extracts, and zip 9 is out of its reach). .cargo/audit.toml now carries the same three, dated and pointing at deny.toml for the justification. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cargo-vet now refuses an unversioned policy for a crate that also has a third-party version. candle-metal-kernels 0.11.0 is in the lockfile twice, from crates.io via candle-nn and from our candle fork. The policy is now keyed to the git revision, since only the fork is first-party. Master has the same lockfile; this step was masked there by the cargo-audit failure. The quinn-proto and webpki-roots exemptions move to the security-fix versions in the lockfile (0.11.19, 1.0.11), which is the repo's convention for dependency deltas. imports.lock is the refresh cargo-vet produced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cargo-vet 0.10.3 requires an explicit audit-as-crates-io policy for a git package whose version matches a published crate. serde_json 1.0.151 from rusty_json_turbo is house code, so it is declared first-party. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Works through the 13 findings PdfCraft reported after switching its Scan & OCR from ocrs to
ffai-carmenta0.12 (mobiledet-svtr, offline), then the 10 it found rerunning this branch. Each finding below is fixed, documented, or measured and refuted. Every change is gated, and every number was measured on this branch.Results (PdfCraft's 42 rendered pages with ground truth; Carmenta alone,
deskew+auto_orienton)PdfCraft's independent rerun (crates.io candle 0.11)
Accuracy: text and word boxes match on all 62 of its pages, and no page got worse. CER is 0.25 % on the 42 scored pages (shipped 0.91 %, ocrs 18.1 %), and word boxes come out at 96 %.
cargo denylicences now pass.Speed: pinned ABBA on whole processes, null arm first, with work counts (word count + text hash per page) matched in every run. The null floor was ≤ 0.8 % on a single page and 0.6 % on the batch.
Peak memory drops 24 % on one page and 8 % on the batch.
Against ocrs (the same models, as ONNX), this branch is 1.12× slower on 1 core and 1.51–1.54× slower on 8 cores, and uses about 1.4–2× the memory. ocrs reads at 13.7 % CER against 0.15 %. Because the gap widens with cores, parallel scaling is the next lever: this branch scales 2.2× from 1 to 8 cores, ocrs 2.9×.
ffai_core::threads::affinity_poolis neutral when the process is unpinned, since its count equals rayon's own. On a process pinned to 8 cores it is 4.7–5.9 % faster, and batch peak memory falls 41 % (3.76 → 2.23 GB). PdfCraft has turned it on.Changes
dwconv.rs,svtr.rs,mobiledet.rs): candle runs a grouped conv as one im2col conv per channel. The new direct depthwise kernel (FMA arm bit-identical to candle) replaces that. SVTR/MobileDet epilogues are fused (8 passes into 1, bit-identical), 1×1 convs run as matmuls, MobileDet's 3×3s go throughconv3x3, and its k=s=2 transposed convs are a matmul plus interleave. SVTR probabilities on 30 real crops are bit-identical to before, so the paddle oracle's 8.857e-5 stands. The MobileDet oracle moved 8.29e-5 → 7.54e-5, with 0 binarised disagreements.OcrOptions::deskew+OcrOutput::skew_degrees(ffai-core): the angle comes from the DB regions' min-area rects. A skewed page is rotated once and its boxes are mapped back. Square pages are untouched.OcrOutput::turns(ffai-core): the quarter turnsauto_orientapplied. Boxes are in the input's coordinates, so before this a 90° page and a 270° page were indistinguishable.deskew::rotated_cornersnow takesturns, which fixes zero-width rectangles on pages that are both turned and skewed.mobiledet-svtr(words.rs): from the CTC emission frames plus decoded spaces, snapped to ink. A formula splice inroute.rsnow keeps the boxes of the words it does not touch.auto_orient: the page is read first, and the orientation decision runs only when confidence < 0.7. Upright pages cost 1.004× (it used to be 2.2×). The turned read replaces the first only when it is more confident.boxes::grid_order), gated on OmniDocBench. Two looser versions were refuted there; the shipped rule changes 1 of 315 pages, for the better.remade-with-rust/carmenta-ppocrv5-mobileon Hugging Face, sha256-pinned, withhf_repoin both ppocrv5 manifests.fetchnow downloads only the files an engine opens; verified from an empty cache.candle-transformersandtokenizers, andffai-modelsdropsdirs(MPLoption-ext, now gone withfetchoff).ffai_core::threads: an affinity-mask-aware thread count and rayon pool for Windows callers.unsafe_code: allowed at each site, so path and git dependents build without the 18 warnings.tools/carmenta_stage_weights.py: stages the weights for a versioned host. It uploads nothing.Not in this PR
tokenizers+ onig, andziphas advisories. Both need candle changes. A root workspace can drop onig with our[patch.crates-io]lines forcandle-coreandtokenizers.Versioning
Versions are bumped in this PR (
ee628f9), so the release step publishes on merge.OcrOutputgainsskew_degreesandturns,OcrOptionsgainsdeskew, androtated_cornerstakes a third argument, so ffai-core goes to 0.9.0 and ffai-carmenta to 0.13.0. Every published crate that depends on ffai-core gets a new version requiring core 0.9; otherwise a caller would mix, say, ffai-media on core 0.8 with carmenta on core 0.9, and the twoImageBuffertypes would not unify:ffai-models 0.7.0, ffai-media 0.7.0, ffai-mercury 1.1.0, ffai-diana 0.9.0, ffai-argus 0.9.0, ffai-bench 0.8.0, ffai-cli 0.9.0, ffai-argus-wasm 0.3.0, ffai-carmenta-wasm 0.4.0, ffai-mercury-wasm 0.3.0, ffai-wasm 0.3.0.
Each crate's CHANGELOG has the entry, and the README install snippets name the new versions.
cargo package --workspacepackages all 13. The structs are not#[non_exhaustive], which would forbid callers'..Default::default()construction.Supply chain
cargo-deny and cargo-audit had started failing on master too, on new advisories. Twelve are fixed in the lockfile: rustls 0.23.46, rustls-webpki 0.103.16, webpki-root-certs 1.0.11, webpki-roots 1.0.11 and quinn-proto 0.11.19. The other three (RUSTSEC-2026-0367/0368/0369) are zip 8.6 extraction bugs fixed only in zip 9, which candle-core 0.11 cannot take. candle never calls
extract, so they are ignored with a dated justification indeny.tomland.cargo/audit.toml, for review on 2026-11-15.🤖 Generated with Claude Code