* Use craft-heif from craft-libs; remove crates/heif
PhotoCraft's HEIF decoder had drifted from LightCraft's by about 700 lines;
both now use craft-heif (storytold/craft-libs#784), built from LightCraft's
copy. HEIC colours now follow the HEVC VUI's range and matrix and libheif's
chroma upsampling. 8-bit files still open as 8-bit documents; a colour
description the VUI specifies now tags the image as an nclx did.
The codecs -> heif standalone exception ends with the local crate, and the
HEIF nextest step drops -p photocraft-heif, which no longer exists.
* Pin craft-heif to the merge of storytold/craft-libs#794
* heif: count the decoder's 16-bit buffer against max_alloc
* heif: match Apple's chroma upsampling; pin craft-heif to storytold/craft-libs#799
* heif: free the decoder's buffer before the HDR conversion and count that conversion's peak
* heif: hold the canvas the decoder really allocates to max_alloc
Keep all 34 catalog deletions; sync crop options and fill rasterization text via craft-i18n 4a642c06dbcb8172f307f827c1167cd5dd8ac341. Adapt the fill prompt catalog test to embedded catalogs and preserve both parity-document updates.
A registered language without a photocraft.tsv is listed apart instead of
showing 0% with every key missing, and a catalog that exists but can't be
read is an error naming the file rather than a silent gap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The i18n module becomes a thin Lang wrapper over craft_i18n::I18n and the
catalogs embedded from craft-i18n. Plurals use trn_plain and PhotoCraft
keeps its own fmt; @id rows come only from PhotoCraft's own rows that keep
the label's shape. Detection, PHOTOCRAFT_LOCALE, tag bounds and the 35
offered languages are unchanged.
Serbian: sr, sr-RS, sr-Cyrl-RS and sr-ME reach sr-latn, the only Serbian
catalog, and a saved interface.language "sr" migrates to "sr-latn".
The menu completeness test becomes a report that never fails, and
`cargo xtask i18n-coverage` is replaced by `cargo xtask i18n-report`, which
reads CRAFT_I18N_DIR through the crate. Docs describe the new system.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* PSD write recipes: keep every exported candidate and pin the base file
A case that failed after export dropped out of manifest.json, losing the
file a reference-editor check most needs. The CLI also reopened the corpus
path after the hash was taken.
- Keep the candidate and its hash once export succeeds.
- Run the CLI on a copy of the hashed bytes, so it never opens the corpus.
- Compare numbers by value, so 17 equals 17.0.
- tracking-split also checks runs 0 and 2 and all three run lengths.
Part of #6130.
* PSD write recipes: compare large integers exactly, reject case-only id clashes
Converting to f64 made 9007199254740993 equal 9007199254740992. Ids that
differ only by letter case name one output directory on a case-insensitive
filesystem, so they are now rejected as duplicates.
* PSD write recipes: test that the CLI receives the hashed bytes
* PSD write recipes: reject the contour library commands
Their path param can read or write any file, including the pinned base and
the corpus, and they edit no document.
* Add recipe-driven PSD export checks
* Exercise PSD recipe runner with the headless CLI
* Make PSD oracle tests isolate temporary paths
* Keep inspect output unchanged; PSD write runner as an xtask library
- info --detail adds text runs, anti-aliasing and full layer-effect settings;
the control channel and MCP inspect output is unchanged.
- The CLI test uses xtask's new library target instead of #[path] includes.
- The runner records revision and dirty state as unknown outside a git checkout.
* psd-write-oracle: name the layer in each check, document versioning, add recipe tests
* Clean up PSD oracle test fixtures on failure with tempfile::TempDir and add docstrings
Main moved crates/ui-egui/src/panels.rs into panels/*.rs, so the color_field entry now names
panels/color.rs (scorecard note too), and main's cognitive_complexity deny split lex()'s block
comment and string skipping into helpers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Validate benchmark names before both measurement and baseline-from-files paths, retaining newer baseline modes.
The i18n coverage scan and prefs_usage stopped reading a file at its first `#[cfg(test)] mod`. A split parent declares `#[cfg(test)] mod tests;` before the rest of its code, so the scans skipped that code, and on main an inline test module in the middle of panels.rs hid the code after it. Both now drop only the body of an inline test module.
The prefs audits in scorecard and prefs_usage count a read when the section and the field appear in the same file. Both now also treat a split module (`foo.rs` with `foo/`) as one file, and prefs_usage leaves out `engine/src/prefs/` as scorecard already does, since that directory now holds the rest of the prefs model.
On main and on this branch: the i18n coverage report is identical (2,142 source keys; the old scan found 2,049 on main), `cargo xtask scorecard --check` is up to date, and prefs_usage passes.
The source scanners in xtask (i18n coverage, scorecard) and engine/tests/it/prefs_usage.rs already walk src/ recursively. The i18n coverage scan also skips directories named tests, as the other two do, so test files moved to foo/tests/*.rs stay out of the key list. The report is unchanged on current main.
* chore: resolve SonarCloud maintainability issues
* fix: format imports and fix effect_entry argument type
* fix: restore test closure and wildcard import that broke compilation
* refactor: split composite_to_surface to reduce cognitive complexity
The 2026-10-05 baseline was recorded at load 224.9 on 14 cores; the scorecard now
says so without changing a number, shows the baseline's run count, and replaces
the claim that the nightly checks regressions with the machine class it actually
runs on.
--update-baseline refuses a contended run (1-minute load at the start above
settings.max_load_per_core per core) unless --allow-contended, which labels it.
--update-baseline --from builds the baseline from several saved results.json
files: the median p50 per scenario and the highest as its noise ceiling, which
regressions are now judged against. It also reports which budgets could be
enforced and which enforced ones are flaky. The single-run path is the N = 1
case of the same function.
The PR's test compares the rendered scorecard with the tree, where the
corpus.rs doc comments and the sha256 manifests agree, so it still passes
with the PR's code reverted. The manifest loop is now pinned_file_counts,
and a synthetic root whose doc comment says 999 files and whose manifest
lists 3 must give 3 (an unpinned corpus keeps its doc comment count). It
fails with the loop skipped.
Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
docs/file-format-parity.md: main's PSD row with the round-trip count
258/258. docs/scorecard.md: regenerated with `cargo xtask scorecard`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's Color panel rework removed hsb_sliders and rgb_sliders, so their
allowlist entries were stale (the check fails on stale entries). One
allowlisted function is left; the scorecard note and docs/scorecard.md
follow.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The report prints, per language, the source keys its catalog lacks (one
line for a language with none). `cargo xtask ci` runs it as its last step
and never fails on it: a missing translation shows in English. A report
that can't run prints a warning.
cargo xtask test and the ci test step run cargo nextest over libs, bins and tests when nextest is
installed (cargo test otherwise, with a notice), then cargo test --doc. The wasm check is one
cargo check with --keep-going over the wasm-safe crates, plus one call per optional web feature.
Clippy runs with --keep-going.
* Use chrono for UTC/calendar conversion instead of hand-rolled copies
Replaces the civil_from_days copies behind the app's log timestamp, the
Measurement Log's date/time, and the perf report date with chrono as a
workspace dependency (reusing the version already in the tree). Output formats
are unchanged and pinned by the existing tests. AGENTS.md now says time goes
through chrono.
Follow-up to storytold/cadcraft#1785
* Never panic on a clock outside chrono's range; keep 5-digit years unprefixed
chrono's From<SystemTime> unwraps internally, so a clock past year 262143
panicked. logging::timestamp and analysis_cmds::now_iso now read the duration
since the epoch and go through the fallible DateTime::from_timestamp, reading
an out-of-range or pre-epoch clock as the epoch as the old saturating code
did. All three ISO helpers (timestamp, now_iso, perf::iso_utc) write the year
with {:04} rather than %Y, which prefixes a '+' past year 9999.
Tests cover the far-future and year-10000 cases for timestamp and iso_utc.
* Don't build an unrepresentable SystemTime in the far-future test
UNIX_EPOCH + Duration::from_secs(u64::MAX / 2) overflows Windows'
SystemTime and panicked before timestamp() ran, failing the Windows
test job. Build it with checked_add and only assert where it exists.
* time: preserve old out-of-range date conversion (no epoch fallback)
`DateTime::from_timestamp(...).unwrap_or_default()` silently reset any
instant past chrono's year range to 1970-01-01, changing output the old
hand-rolled Howard-Hinnant code converted exactly (e.g. 10_000_000_000_000 s
was 318857-05-20 17:46:40, not the epoch). Restore the old conversion domain
with a cycle-based helper that routes the calendar through chrono (the
calendar repeats every 400 years = 146_097 days, so month/day/time come from
chrono and only the year is carried in i128, keeping every i64 exact).
- apps/photocraft/src/logging.rs: `timestamp` via `utc_parts`; keep the
pre-1970 epoch clamp; u64 secs past i64::MAX saturate, not epoch.
- crates/engine/src/analysis_cmds.rs: extract pure `iso_from_unix` (cycle
based) from `now_iso` so the conversion is tested directly.
- xtask/src/perf.rs: `iso_utc` cycle-based, accepts the full u64 range the
old code did.
Pinned old outputs in tests:
logging u64::MAX/2 s -> 292277026596-12-04T15:30:07.000Z (was epoch),
10_000_000_000_000 s -> 318857-05-20T17:46:40.000Z
iso_from_unix 10_000_000_000_000 -> 318857-05-20T17:46:40Z,
-10_000_000_000_000 -> -314918-08-13T06:13:20Z, i64 extremes
iso_utc u64::MAX -> 584554051223-11-09T07:00:15Z (was epoch),
10_000_000_000_000 -> 318857-05-20T17:46:40Z
* Don't build a year-318857 SystemTime unchecked in the logging test
Windows' SystemTime only reaches about year 30828, so
UNIX_EPOCH + Duration::from_secs(10_000_000_000_000) panicked before
timestamp() ran and failed the Windows test job. Build it with
checked_add and assert where the platform can hold it. The year-10000
case fits everywhere and stays unconditional.
* Name the 400-year range extension as the one exception to the time rule
The rule said never to hand-roll calendar math, but the range fix
(shifting by whole 400-year Gregorian cycles so dates past chrono's year
range keep their old output) is cycle arithmetic, so the rule and the code
disagreed. The rule now names that exception: the library still computes
month, day and time, and only whole cycles are carried in the year.
* Say the library computes month and day; time-of-day is plain arithmetic
Narrows the range-extension wording: the date library computes the
calendar fields, and splitting seconds into hours, minutes and seconds
is ordinary arithmetic, which is what the code does.
- gpu: the test modules share one GPU_LOCK, and timing and vector_mask
take it too, so one process drives one wgpu device at a time.
- ui-egui: the wgpu tests in pattern_preview_canvas, adjust_preview_gpu,
marquee_autoscroll and layout_perf take gpu_lock().
- affinity compound_limits: the child process selects its test by the
module path (compound_limits::<name>); with --exact, the bare name
matches no test and the child passes without running the check.
- xtask scorecard reads crates/io/tests/it/corpus.rs.
- Docs and comments name the new test paths and the --test it filters.
crates/engine/src/file_cmds.rs: batch keeps the save-point closure (takes the output path) and also copies the session's authorize hook into each scratch session, as main now does.
crates/raw/src/sensor.rs: both sides cap the memory of retained tiles; kept the existing retained_segment_bytes check (checked multiplication, same limit) and dropped main's duplicate inline loop.
Row 16 (9f6a3c79): preserve platform filename semantics in corpus scans
(no backslash-to-separator rewrite on Unix) and enforce destination
containment before copying files into the corpus directory.
Row 20 (6e2a985b): skip directory symlinks, dedupe visited directories by
canonical identity, and cap total directories/files so symlink cycles can
no longer drive exponential traversal.
Fixes findings 9f6a3c79-8bdf-4751-8eb4-87d3154074c9 and
6e2a985b-6b14-4b97-af07-7ca8333b9b75.