Covecto v0.1.2: The Optimizer That Silently Wasn't Running
The --optimize flag was silently skipped on Covecto's streaming path for twelve weeks. v0.1.2 fixes it and adds 58 parity tests so it stays fixed.
On July 23, Covecto 0.1.0 shipped with an --optimize flag. In the library API, the flag worked. In the CLI, it did nothing: every SVG the command line wrote went from tracer to disk with no optimization pass at all. That lasted exactly twelve weeks, until v0.1.2 on October 15.
The fix itself took one pull request. The more durable part is what shipped next to it: a parity suite that runs both execution paths across 58 configurations and checks their counters against known-good values, so the two paths cannot drift apart quietly again.
Honest framing first: Covecto is small. Three GitHub stars, 61 downloads on crates.io as of this writing, MIT licensed, written in Rust. This post treats a patch release as a story anyway, because the failure mode, a flag that reports success while doing nothing, is one that any tool with more than one code path can produce.
Two paths, one silent divergence
Covecto converts images to SVG with two engines: a pixel-exact tracer and a spline engine based on vtracer. Like most batch tools, it also has two ways to deliver output. vectorize() builds the SVG in memory and hands it back. vectorize_to() streams it straight to disk, which matters when you convert a few hundred icons in one run and would rather not hold every result in RAM.
The optimizer lived on the in-memory path. When the CLI called the streaming path, --optimize was accepted, parsed, and ignored. Exit code 0. Valid SVG. It opened fine in a browser.
The only evidence was size. The file was bigger than it should have been, and you would only notice by comparing against an optimized reference. Nobody files a bug report for that, and in twelve weeks, nobody did.
Why it survived twelve weeks
Both bugs on that path were invisible by construction.
The optimizer skip was invisible because the output was still correct SVG. Optimization changes size, not correctness. A visual test suite passes either way.
The metadata bugs were invisible because wrong counters do not draw anything. svg_byte_size was off by one. path_count counted translucent paths twice. The image renders identically either way; the numbers lie.
There is a discussion on Hacker News right now (246 points as of October 18) about a tool that quizzes you on your AI's code before you ship it. The premise: as code gets written faster, verification becomes the bottleneck. Our takeaway from this release is narrower. Whenever one capability has two implementations, something has to force them to agree, and it should not be a human diffing output files by hand.
The parity suite
Applying optimization on the streaming path was the small change (#43). The part we expect to matter longer is the test structure added alongside it (#47, #49):
- 58 cases covering the configuration matrix of both engines
- every case runs through
vectorize()andvectorize_to() - counter assertions grounded in known-good values, so byte size and path counts are checked against ground truth rather than against whichever path happens to be running
That last property is the difference between a mirror test and a real check. If two paths only assert agreement with each other, they can agree on the same wrong number. Ground truth catches shared blindness.
We took the same stance in public before, when we published our benchmark numbers and then invited people to break them. Auditing your own output is cheaper the day you build it than the day a user does it for you. For what the optimization pass changes in the output files, our earlier guide to SVG optimization after vectorization covers the details.
Upgrade notes
- Install or update with
cargo install covecto, or grab the release binaries: each archive shipscovecto(CLI) andcovecto-api(HTTP server) for Linux x86_64 and ARM64, macOS Apple Silicon, and Windows. - If you batch-converted with
--optimizebetween July 23 and October 15, re-run the batch. The SVGs were valid, just larger than they should have been. - CI, release, and Docker builds now compile with
--lockedagainst the checked-inCargo.lock(#46, #50), so a fresh clone and a release artifact resolve the same dependency tree.
Where to look
The whole fix is visible in the repo: release v0.1.2, the linked pull requests above, and the parity tests in the core crate. The documentation site covers the CLI and the HTTP API.
If you maintain a tool with parallel code paths, a fast path and a safe path, memory and streaming, batch and single, the takeaway fits in one line. The paths will drift. The suite is how you notice before a user does.