On this page

Wire has one rule about itself: the code does what the docs say, and every part of the build agrees with every other part. When a scanner reads your content differently than the renderer does, that is not a small bug. It is Wire contradicting itself, and the output cannot be trusted until the contradiction is gone.

This batch of releases, v4.3.13 through v4.3.20, is mostly that work: closing the gaps where one part of Wire disagreed with another.

Scanners now read code the way the renderer does

Wire treats fenced and inline code as documentation, not instructions. The build renderer always knew this. Three other parts of Wire did not.

The image scanner used to read a !makeIMG[...] written inside a code block as a real generation request (#426). The discovery engine counted a ## heading inside a code fence as a real page anchor, then built a steps.md that failed its own lint rule (#430). Worst of all, the content sanitizer that auto-fixes your links ran over code blocks too: a guide that documented [text](/example/) had that example silently rewritten or stripped on every save, and the damage compounded each time (#434).

All three now skip code spans the same way the renderer does. A page that teaches Wire syntax keeps its examples intact.

The same content now builds the same bytes

Two values in every build came from Python's builtin hash(), which is randomized per process. So the same article, rebuilt with no edits, produced different output every time.

The time-of-day stamped onto past-dated articles jittered run to run, so article:published_time, JSON-LD datePublished, and RSS pubDate changed for no reason (#432). The DOM ids that scope each :::tabs block changed on every rebuild, churning the HTML and busting content-keyed caches (#433). Both now derive from hashlib: identical input, identical output. Wire treats builtin hash() as forbidden anywhere it feeds the build.

Honest about what it does not do

Wire's docs promised a --dry-run that previewed content changes without spending anything. That was never true. The flag did the full paid AI work and only skipped the commit, writing a .preview file that no command could ever turn back into your page. You paid twice for one result.

So we deleted the phantom feature. Wire now refuses --dry-run on content commands with a message that tells you what actually works: run it for real, read the change with git diff, and undo it with git checkout (#427). The one exception is images --dry-run, which makes no AI call and only estimates cost. A doc-drift test now scans every command example in the docs and fails if it names a flag that does not exist, so this gap cannot reopen (#425).

Fewer false alarms, more reach

A handful of fixes stop Wire from blocking good work. The JPEG dimension reader now understands extended-sequential and lossless frames, not just baseline, so JPEGs from cameras and Photoshop stop tripping the missing-dimensions rule (#431). Web search now works on installs that use the Claude CLI instead of an API key, so news and refine are no longer dead on those machines (#428). The forbidden-topic guard refuses a banned draft before generating it, not just at build, so you spend no tokens on a page Wire would reject (#415). And crosslink skips pages with no keyword overlap entirely, cutting wall time on large sites (#298).

What you do

Upgrade and rebuild. Nothing here touches your content or your config.

python -m wire.chief update
python -m wire.build

The throughline is Wire's oldest promise turned inward. A tool that lints your content for consistency has to be consistent with itself first. The full command set is in the workflow guide, the image and component rules are in the components guide, and the work is tracked across issues #415 through #434 if you want the details.