Commit Graph
127 Commits
Author SHA1 Message Date
Martin BallandClaude Opus 5.5 9dd39df78d feat(i18n): look up translations through the craft-i18n crate
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>
2026-11-08 16:16:46 +00:00
David Mann 017c4aab9e Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	docs/scorecard.md
2026-11-06 22:57:46 +01:00
Khawar Zamman Wani 146d49f5a2 PSD write recipes: keep every exported candidate and pin the base file (#11607)
* 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.
2026-11-06 17:23:40 +01:00
David Mann 66c328dc2b Merge commit '9bef3b2dc8b84c1b6cefc8ce0dc2b01d089cd9e8' into HEAD
# Conflicts:
#	docs/scorecard.md
2026-11-06 02:29:28 +01:00
Khawar Zamman Wani f54d605e2c Add recipe-driven checks for PSD exports (#10307)
* 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
2026-11-05 17:11:56 +01:00
David Mann 4bbe9c78de Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	docs/scorecard.md
2026-11-03 20:32:22 +01:00
David MannandClaude Opus 5.5 77339962cc xtask: panel-commands allowlist follows main's panels/ split; lexer helpers keep clippy's complexity limit
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>
2026-10-29 16:49:12 +01:00
David Mann 5dd023f949 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-29 16:45:03 +01:00
FredApps 32722ffcbf Merge approved PhotoCraft PR #9019: fix(xtask): reject unknown performance benchmark selections
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.
2026-10-29 04:21:14 +01:00
FredApps 9f076017fe Merge approved PhotoCraft PR #9015: fix(xtask): reject leading zeroes in numeric prerelease identifiers 2026-10-29 03:29:44 +01:00
aiongg a9e6f73ac5 Merge remote-tracking branch 'upstream/main' into HEAD 2026-10-28 02:31:52 +00:00
aiongg 7008da37ab perf: baseline skips rows missing from some runs 2026-10-28 02:14:22 +00:00
David MannandClaude Opus 5.5 523c2cc8d3 Merge main into feat/auto-219-8-panel-command-check
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 03:08:53 +01:00
aiongg 065c2c2beb test: keep split modules visible to the source scans
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.
2026-10-26 22:54:56 +00:00
aiongg 1dc1a17690 refactor(xtask): move corpus.rs tests to corpus/tests.rs 2026-10-26 22:30:14 +00:00
aiongg 25e8edb784 refactor(xtask): read the i18n registry from i18n.rs 2026-10-26 22:13:07 +00:00
aiongg df8ffb2a63 test: scan source directories recursively
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.
2026-10-26 22:13:07 +00:00
Brennan StehlingandClaude Opus 5.5 f342dba86e xtask perf: a row that failed in any run gets no baseline; judge contention on the load before the build (#7494)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 22:33:56 -07:00
aiongg cece50d513 Merge pull request #9386 from davidmann4/integration-batch-8
Integration batch 8: 37 reviewed community PRs on main
2026-10-25 20:04:41 -04:00
Dylan M. Taylor 3877bb1dbe chore: resolve SonarCloud maintainability issues (#9044)
* 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
2026-10-25 19:38:31 -04:00
Brennan Stehling 2ab11c7995 xtask perf: rustfmt (#7494) 2026-10-25 16:04:30 -07:00
Brennan StehlingandClaude Opus 5.5 7060624283 xtask perf: a refused baseline still reports the run's perf failures (#7494)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 16:04:30 -07:00
Brennan Stehling 6caa8056ce scorecard: label a contended baseline and say where the regression check runs (#7494)
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.
2026-10-25 16:04:30 -07:00
Brennan Stehling 9335daaf41 xtask perf: a baseline knows how trustworthy it is (#7494)
--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.
2026-10-25 16:04:30 -07:00
cfadb4c33f Integration: a test that tells pinned corpus counts from doc comments (#8658)
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>
2026-10-25 21:27:53 +01:00
David MannandClaude Opus 5.5 a60b72483e Integration: merge main, resolve conflicts (#8658)
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>
2026-10-25 12:46:18 +01:00
David MannandClaude Opus 5.5 f26bd3e257 xtask: drop the panel-commands allowlist entries main fixed
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>
2026-10-24 02:57:09 +02:00
David Mann 74fe5dc853 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	.github/workflows/ci.yml
#	docs/scorecard.md
#	xtask/src/main.rs
2026-10-24 02:56:09 +02:00
Lucenx9 364c4be1c1 fix(xtask): reject unknown performance benchmark selections 2026-10-24 01:48:25 +02:00
Lucenx9 afb4364a87 fix(xtask): reject leading zeroes in numeric prerelease identifiers 2026-10-24 01:22:09 +02:00
aiongg 8debb6c420 xtask: the panic hunt without nextest builds with the HEIF decoder 2026-10-23 20:10:26 +00:00
aiongg 4b51647955 Merge remote-tracking branch 'upstream/main' into xtask-ci-gate
# Conflicts:
#	xtask/src/main.rs
2026-10-23 19:44:29 +00:00
aiongg a75a414310 CI: cargo xtask ci is the gate, and the workflow runs it 2026-10-23 19:29:31 +00:00
aiongg cb297eeac1 xtask: i18n-coverage lists missing strings; ci prints them last
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.
2026-10-23 19:07:23 +00:00
aiongg 175c9c1e75 xtask: test with nextest, doctests separately, one wasm check call
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.
2026-10-23 06:00:38 +00:00
David MannandClaude Opus 5.5 beb50a43d3 Allowlist main's Color panel RGB sliders in panel-commands; regenerate scorecard
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 00:06:24 +02:00
David Mann 32281291ae Merge origin/main into feat/auto-219-8-panel-command-check
# Conflicts:
#	docs/scorecard.md
#	xtask/src/main.rs
2026-10-23 00:04:23 +02:00
Lucenx9 4cf1087342 Merge origin/main and address CodeRabbit corpus inventory feedback 2026-10-22 20:02:52 +02:00
Dylan M. Taylor 6649b78526 Use chrono instead of hand-rolled UTC conversion (#8234)
* 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.
2026-10-22 13:54:10 -04:00
Lucenx9 52f438c864 fix(xtask): derive scorecard corpus counts from pinned manifests 2026-10-22 19:45:06 +02:00
aiongg ffa400c754 Keep tests correct and docs current with one test binary per crate
- 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.
2026-10-22 16:55:34 +00:00
aiongg 934eb71d84 Integration: merge main
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.
2026-10-22 15:39:51 +00:00
Dylan M. Taylor ef6092b125 security(xtask): validate corpus paths and bound symlink traversal
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.
2026-10-22 07:11:30 -04:00
aiongg 586b4f59a2 Merge PR #6946: docs(xtask): point the layers help text at docs/architecture.md 2026-10-22 02:22:02 +00:00
aiongg f32f882f3a Merge main into integration-10
# Conflicts:
#	apps/photocraft-web/src/web.rs
#	apps/photocraft/src/services.rs
#	crates/engine/src/file_cmds/tests.rs
#	crates/engine/src/float_cmds.rs
#	crates/engine/src/float_cmds/tests.rs
#	xtask/src/corpus.rs
#	xtask/src/corpus_pins.rs
#	xtask/src/main.rs
2026-10-21 21:56:26 +00:00
David Mann 779471d19f Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	xtask/src/main.rs
2026-10-21 22:39:40 +02:00
skylar taylor-barrick 6d8cbac7dc Merge pull request #2945 from diegochagas/feat/xcf-read
Open GIMP XCF files: layers, groups, masks, blend modes, channels and selection
2026-10-21 14:35:32 -06:00
Diego Chagas ca18689304 Open GIMP XCF files: layers, groups, masks, modes, channels and selection (#1076)
PhotoCraft reads GIMP's .xcf (and .xcf.gz) files, read only, through a new
reader in photocraft-io written from the public XCF specification: versions
0 to 26, uncompressed, RLE and zlib tiles, 8/16/32-bit integer and
16/32/64-bit float precisions (linear precisions get a linear profile),
pixel layers at any position, groups by item path with pass-through,
layer masks applied or disabled, opacity, visibility, locks, colour tags,
the 2.10 and legacy blend modes mapped to Photoshop's (modes without an
equivalent open as Normal with a note), the ICC profile, resolution,
guides, the comment, extra channels and the saved selection; indexed
images open as RGB. Text, vector and link layers open as their pixels;
layer effects, paths and floating selections are reported.

Fixtures are written by GIMP 3.2 itself (scripts/xcf_fixtures.py), each
with GIMP's merged rendering as a PNG. In gamma-encoded blend and
composite spaces our composite matches GIMP within 0.5/255; GIMP's
linear-light default, its Clip to backdrop and its clamp-after-alpha
for unbounded modes are documented and measured in docs/xcf.md.
2026-10-21 16:57:22 -03:00
David MannandClaude Opus 5.5 9b21358035 Address CodeRabbit review: document the panel scan's blind spots
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 12:43:58 +02:00
David MannandClaude Opus 5.5 b45be327b6 Address CodeRabbit review: count line continuations in string literals
A backslash before a newline inside a string skipped the newline without
counting it, so every later violation reported a line one too low.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 11:37:15 +02:00