Skip to content

Release History

Stevie Remich edited this page Aug 28, 2026 · 1 revision

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.

Verification status

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.

Releases

1.3.0 (release)

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.

1.2.1 (release)

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×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.

1.2.0 (release)

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.

1.1.0 (release)

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.

1.0.0 (release)

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.

0.6.0 (pre-release)

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.

0.4.0 (pre-release)

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 DateTimeDigitized universally
  • 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.

0.1.0 (pre-release)

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.

Clone this wiki locally