-
Notifications
You must be signed in to change notification settings - Fork 0
Release History
One entry per release. Each says what the release contains and — more usefully — what it is safe to be trusted for. Everything before 1.0.0 was a pre-release that had not been verified against a full dataset; 1.0.0 onwards are full releases.
See CHANGELOG.md in the repository for the itemised change list, and
docs/PROJECT-HISTORY.md for the milestone record and corpus statistics.
| Tier | What it proves | Status |
|---|---|---|
| 1. Unit | Parsers, tile reassembly, timestamp and naming logic, batch engine, resume state, audit reporting, output control, filename and folder patterns, and the desktop front end's Qt-free half |
Passing. Backed by scripts/mutation_check.py, which breaks 17 load-bearing rules on purpose and requires the test named for each one to go red |
| 2. e2e | Full pipeline over the committed person-free fixtures — both colour spaces, six of seven declared sizes — plus the desktop front end driving a real child process, cancellation included | Passing. Scan through convert on both trees, pixel decoding with PhotoYCC coverage, metadata embedding, independent tag read-back on both TIFF and JPEG, and the colour oracle exercised in both directions |
| 3. Sample batch | Real archive files spanning every album, every declared size, both colour spaces and all four transform outcomes | Passing on the reference corpus at 1.2.1 — 64/64 converted clean, 0 read-back violations, worst chroma correlation 0.739 against a gate of 0.5, thumbnail oracle improved on 9 of 9 cropped files |
| 4. Full dataset | An unattended batch run over the whole corpus with an audit report, plus a person looking at the converted photographs in a real photo application |
Both halves passed. The batch half at 1.0.0 (687/687, complete: true, unexplained_failures: 0); the eyeball half after 1.2.1 |
Two things worth stating plainly about that table.
Tiers 3 and 4 read a private photo archive and cannot run in CI or on a contributor's machine. That is expected, not broken. A green pull request means tiers 1 and 2 passed and nothing more.
The tier-4 eyeball pass is of the output as it stood at 1.2.1, not a permanent property of the code. Any change to the decoder, the colour conversion, or the viewing transform puts it back to outstanding.
A note on the read-back tool. Through 1.2.1 the independent metadata
read-back used pyexiv2, and the entries below are accurate as of their own
dates. It was later removed from the shipped package — pyexiv2 is GPL-3.0 and
bundles a GPL-2.0-or-later exiv2.dll, which would relicense the Windows
executable — and replaced by Pillow with defusedxml. pyexiv2 remains a
development-only dependency used as a third opinion. The rule is unchanged:
validate with a different tool than the one that wrote.
What it contains: The release that makes this repository publishable, and everything in it was found by auditing the project as an outsider would read it rather than by using it.
Two of those findings were serious. Every photograph failed for anyone outside
the United States. The timezone table held seven US zones and raised for
anything else, and the batch engine catches that exception per file — so a run
in London or Tokyo wrote all of its images and then reported a 100% failure
rate, permanently, because nothing was ever marked done. Offsets now resolve
through zoneinfo against a pinned tzdata, with historically correct DST: a
2001 Chicago photograph gets -06:00, which today's rules would get wrong. The
zone comes from --timezone, then FPX_DEFAULT_TZ, then the system, and an
unresolvable one is still refused rather than guessed.
And the published executable was conveying GPL code from a repository that
offered no licence at all. The metadata read-back used pyexiv2, which is
GPL-3.0 and bundles a GPL-2.0-or-later exiv2.dll. Pillow with defusedxml
reads the tags back instead, and packaging/licence_guard.py fails the build if
anything named exiv2 reaches the bundle again. The project is now Apache-2.0,
with NOTICE, THIRD-PARTY-NOTICES.md, the LGPL and GPL texts Qt obliges it to
carry, and a Help → Licences dialog so a bare .exe travels with its notices.
Smaller, and all of the same kind: a missing ExifTool now stops the run once
instead of failing all 687 files; default paths no longer write into
site-packages; an unrecognised colour space raises a per-file warning instead
of silently assuming NIF RGB; --max-path now accounts for ExifTool's temporary
file, which is 13 characters longer than the final path.
What it is safe to trust for: Everything 1.2.1 was, in more places —
specifically, outside the United States, from a pip installed copy, and with a
licence that says what you may do with it.
Verification: Tiers 1 and 2 passing, on Windows and Linux, including a job
that scans the whole of git history for personal content. The read-back switch
was verified by round trip rather than by inference: all ten tags written and
re-read on both TIFF and JPEG, and the build guard was mutation-tested by
forcing pyexiv2 back into the bundle and confirming the build fails.
Tiers 3 and 4 were not re-run for this release, and this entry does not
claim them. What changed in the pixel path is a warning, not a conversion — the
decoder produces the same pixels it did at 1.2.1 — but the timezone fix changes
the OffsetTime* values written beside every timestamp, and that is an output
change that no eyeball pass has looked at.
Removed, and worth knowing before you fork it: three test fixtures containing a person, found by viewing all forty at full resolution rather than trusting the notes — which had recorded the problem as "a hand at the frame edge" and were wrong about both the body part and the location. One of them was the only fixture covering the viewing-transform crop. Those tests are stood down with a stated reason rather than deleted, and a guard fails if a cropped fixture returns without the documentation being updated. The rule outranks the coverage.
What it contains: Two fixes to things a person meets before the converter has done anything at all.
The desktop window opened too small to read: the cards were squashed into slivers, three radio buttons collapsed to underscores, and the naming card was empty. A fixed minimum size was right for the four sections 1.1.0 had and silently wrong once 1.2.0 added a card, so Qt was allowed to open the window nearly 300 pixels shorter than its contents needed. The size now comes from the layout, measured at the width the window will actually have, and the contents sit in a scroll area so a display too short for them scrolls rather than squeezing them.
Custom also stopped asking a question it had no business asking. It offered a
further choice between an archive copy and a shareable one, which is the choice
directly above it, and let somebody tick neither and meet a greyed-out Convert
button. All three modes now write one image; Custom is the one where you say
which. Which folder it lands in follows the framing — archive/ keeps the full
frame, sharing/ gets the crop — so the same two answers reach the same place
however they were given. Both trees in a single run remains a command-line
thing.
And the executable carries its version in its filename, so two downloads a year apart are not two files with the same name.
What it is safe to trust for: Everything 1.2.0 was. No conversion logic changed: the diff is the window's geometry, which of its controls exist, some shorter explanatory lines, and the name of the built file.
Verification: Tiers 1, 2 and 3 all passing; the mutation check green. Tier 3 was run rather than argued about, because output pathing is batch-path code even when the change is only a display string. The executable was built locally and run before tagging. The window's new sizing is covered by tests that state the screen rather than inherit the headless one — the offscreen platform reports×ばつ800, and the first version of those tests could not have failed in CI.
Tier 4's eyeball half was completed shortly after this release and passed.
What it contains: The first release driven by somebody running the packaged application rather than the test suite, and it shows in what it fixes. ExifTool no longer opens a console window for every photograph — a 687-file run flashed 687 of them. The desktop app's dropdown menus, which had been rendering their text in a colour that vanished against the field, are readable.
Three changes to what a run does. A conversion now writes only the images asked
for: the .fpx source copy and the .fpx.json sidecar became --source-copy
and --sidecar, off by default, where before every photograph produced four
files. The app offers three exclusive choices — Archive copy, Shareable copy,
Custom — instead of two checkboxes and four menus. And both the output folders
and the filenames became user-definable: --folder-scheme with
--folder-template, and --name-template. The window shows a live preview of
two photographs — one album-dated, one with nothing to date it — built by
calling the same function the conversion calls.
What it is safe to trust for:
- Everything 1.1.0 was, with the same defaults: an unchanged default filename (verified byte-for-byte against the old expression over all 687 manifest entries and a set of deliberately awkward names) and an unchanged default folder layout
- Filing photographs by year, by year and month, flat, or by your own pattern, with output names unique by construction in every scheme
- A refusal, before a run starts, of any pattern that would lose the archive's filenames or put files outside the destination
- Resuming: a change to the output specs, the filename pattern, the folder arrangement, or a request for a file the previous run did not write all invalidate the resume rather than silently skipping
What no test can cover: the console-window and dropdown fixes. Both were found by running the application, and confirming them needs the application run again.
Verification: Tiers 1, 2 and 3 passing, plus the mutation check green. Tier 3
was a trigger for this release and ran: 64 files, 64 clean, 0 read-back
violations. Two bugs found by pre-release audit and fixed before the tag: adding
--source-copy to a finished destination wrote nothing at all, and a new
trailing-character normalisation changed the default filename for names ending
in a dot or a space.
What it contains: A desktop application, shipped as one standalone Windows
executable — no Python, no pip, no terminal. Two folder pickers, output
controls, Convert and Cancel, a progress bar following the real per-file trail,
a log pane, and a plain-language summary read back from audit_report.json. It
wraps the CLI rather than reimplementing it: every conversion is
fpx_converter running as a child process with the arguments a person would
have typed. New CLI hooks a front end needs: convert --progress and
convert --stop-file PATH.
What it is safe to trust for:
- Converting an archive without touching a terminal, on a machine with nothing installed but ExifTool
- Cancelling a run and still getting an audit report describing what was done
- The read-only source rule reaching the front end as a call to
config.ensure_outside_source, not a second copy of the check — replace it with a local copy and exactly two tests fail, a count measured by mutation
Verification: Tiers 1 and 2 passing, including the front end driving a real
child process and a cancellation that must still leave a report. The release
workflow builds the executable and converts fixtures through it before
creating the release — a --version call cannot see a missing binary
dependency, and a build that fails must not leave a published release with
nothing in it. Two full adversarial audit rounds, 19 findings, all closed.
What it contains: The whole archive converted in one unattended pass. 687
converted, 0 failed, 0 with warnings, complete: true,
unexplained_failures: 0, in 641 seconds.
What it is safe to trust for:
- The pipeline over a full real corpus rather than a sample. Every predicted number matched what the run produced: 70 cropped outputs, 609/56/22 transform outcomes, 617/68/2 date sources
- The pixel-identical-pairs estimate replaced by a measurement — 251 files in 120 groups, reported as expected duplicates and not as faults
Verification: Tiers 1, 2 and 3 passing; the automated half of tier 4 passing over all 687 files. The eyeball half was outstanding at this release, knowingly: it had gated releases until this one, and rather than keep a rule the releases were stepping over, the rule was removed deliberately. It was done after 1.2.1 and passed.
What it contains: Combines two milestones built and audited as one unit: the
0.5.0 batch engine with resume-by-hash and audit reporting, and the 0.6.0
QA gallery with per-album date entry. Unattended batch conversion of the full
manifest with automatic resumption after a crash or kill; never stops on one bad
file. Output format and framing became independent settings for the first time.
The HTML gallery shows every photograph as an embedded thumbnail, filterable by
album and audit status; missing capture dates are collected via a form and
persisted in album-dates.json, which convert reads back. The output tree
started following source folder names rather than deriving album structure from
timestamps.
What it is safe to trust for:
- Unattended batch conversion with automatic resumption if a run is killed (keyed on source SHA-256; only the file in flight is retried)
- Proper handling of the expected pixel-identical output pairs
- Independent control of both trees' format and framing, defaults unchanged
- Complete audit reporting across multiple sessions
- Folder-name-based album organisation preferring the most descriptive folder a file belongs to — this fixed a regression affecting 52 photographs
- Per-album date entry with JSON export; dates land in EXIF only if day-precise
What does NOT work: Full-corpus conversion had not been run.
Verification: Tiers 1, 2 and 3 passing; tier 4 not yet reached.
What it contains: The full conversion pipeline. Metadata extraction engine (custom OLE property-set parser for all 10 property sets), pixel decoder (pure-Python tile reassembly with JPEG splicing, raw RGB, and single-colour fill support), and the dual-output writer (archival Deflate TIFF plus quality-95 4:4:4 JPEG, both tagged sRGB). Metadata embedding via ExifTool with independent validation. Correct filesystem mtime. Complete raw JSON sidecars. Embedded DIB thumbnail extraction. Ground-truth date checking against folder names.
What it is safe to trust for:
- A 50-file sample spanning every album, all 7 declared sizes, both colour spaces and all four transform outcomes
- Metadata extraction and preservation into standard EXIF/XMP/IPTC tags
- Verified image geometry for the 90° rotation, including the 14 rotated-and-cropped files, checked against the embedded-thumbnail correlation oracle — which compares greyscale only
- Defensible date assignment: capture dates only from day-precise folder names
or embedded scan dates; import stamp on
DateTimeDigitizeduniversally - Viewing-transform crops on all 70 affected files (56 axis-aligned, 14 rotated-and-cropped): full frame preserved in the archive TIFF, cropped JPEG in the sharing tree. 70 of 70 improved against the thumbnail oracle, worst correlation 0.981
Explicitly NOT safe to trust for colour on the PhotoYCC files. Per-file colour-space detection was implemented and applied, but those files had not been looked at by a person — and that caution turned out to be justified.
What does NOT work: The batch engine (0.5.0) and the QA gallery (0.6.0). No resume-by-hash, no unattended run; batch processing required manual per-command invocation.
Verification: Tiers 1, 2 and 3 passing; tier 4 not yet reached. Shipped after three audit rounds — the third found that the colour check added by the second could not detect colour.
What it contains: Source ingestion. A read-only scan of the source archive
walks every .fpx file, builds a SHA-256-keyed manifest, and copies one file per
distinct hash to a local deduplicated store with verification. Filename
selection preserves human-authored content.
What it is safe to trust for:
- Building and verifying a deduplicated manifest of the source archive
- Confirming zero corruption or bit rot (1,265 files scanned, all valid OLE2 documents)
- Producing a local read-only copy for safe offline archival storage
What does NOT work: Pixel decoding, metadata extraction, TIFF/JPEG output, the batch engine, the QA gallery, and the dating UI.
Verification: Tiers 1 and 2 passing; tiers 3 and 4 not yet reached.