mdtablefix 0.6.1

`mdtablefix` unb0rks and reflows Markdown tables so that each column has a uniform width. When the `--wrap` option is used, it also wraps paragraphs and list items to 80 columns.
Documentation
# Changelog

## v0.6.1

- Fixed: `--renumber` no longer ends an ordered list at a heading or thematic
  break indented into one of its items. Such a block now ends only the lists
  whose current item it is not indented into, judged by the item's content
  column. Since #106 every such block up to three spaces deep reset all list
  state, so the items after it restarted at one.
  ([#450]https://github.com/leynos/mdtablefix/issues/450)
- Fixed: `--wrap` breaks lines only where the source already has whitespace, so
  a wrap can no longer add a space to the rendered text. `([ref][4])`,
  `` `a`/`b` ``, `-[r]->`, `**`code`**` and `][4]).` stay whole, even when that
  leaves a line longer than the target width. A line the wrapper breaks keeps
  no trailing spaces, so a run of spaces can no longer become a hard break.
  Re-running `make fmt` after upgrading may rejoin lines that 0.6.0 split at
  these seams; the rendered text of such a file becomes the source's again.
  ([#561]https://github.com/leynos/mdtablefix/issues/561)
- Fixed: `--wrap` keeps both lines of a Setext heading. It joined a `===`
  underline onto its text as one paragraph, so `Arc Gambit` over `==========`
  became `Arc Gambit ==========`. The `--breaks` half was fixed earlier on main.
  ([#562]https://github.com/leynos/mdtablefix/issues/562)
- Fixed: wrapping a bracket label is idempotent. The mechanism is the #561
  change, which lets the wrapper break only at source whitespace, so `[a]`
  written after a space or touching its text no longer moves on a second pass.
  This release pins both reproductions with regression tests.
  ([#507]https://github.com/leynos/mdtablefix/issues/507)
- Fixed: `--breaks` keeps the indentation of a thematic break indented to a
  list item's content column. It had written a list item's break at column 0
  since `--breaks` was introduced in #57, which took the break and the rest of
  the item out of the list and split the list in two.
  ([#572]https://github.com/leynos/mdtablefix/issues/572)
- Fixed: the table pass no longer drops a line made only of pipes and spaces.
  In a pipe-led block with no delimiter row it deleted paragraph `|`
  characters, and in a table it deleted an empty body row or promoted the first
  body row over an empty header. Such a block is now left as written. A
  regression from #502.
  ([#582]https://github.com/leynos/mdtablefix/issues/582)
- Fixed: the Verus harness and static-regex tests run their stub scripts
  through `bash` or `sh` instead of executing files they have just written.
  Another test thread forking in the same instant could make the execution fail
  with `ETXTBSY`, which failed
  `make_verus_selftest_accepts_only_a_rejected_smoke_proof` intermittently.
  ([#586]https://github.com/leynos/mdtablefix/issues/586)
- Fixed: `--renumber` ends an ordered list at any block left of its item's
  content column: after a blank line a paragraph, fenced code block, table,
  block quote, HTML block or bullet list, and without one a block that can
  interrupt a paragraph (a fence, block quote, bullet item with text, or HTML
  block). The list after it restarts at one instead of continuing the count and
  changing its rendered `start`. Renumbering had continued across all of these
  since it was introduced; only paragraphs that began with a letter or digit
  ended a list. ([#563]https://github.com/leynos/mdtablefix/issues/563)
- Fixed: `--renumber` leaves alone a numbered line that continues a paragraph,
  such as a wrapped sentence whose next line starts `12.`. Only a list starting
  at 1 can interrupt a paragraph, so the line is paragraph text; rewriting it to
  `1.` had turned it into a list item since renumbering was introduced in #54.
  ([#573]https://github.com/leynos/mdtablefix/issues/573)

## v0.6.0

### Added

- An enforced environment-access policy. `clippy.toml` prohibits the six
  `std::env` read and mutation methods, the workspace lint table denies
  `clippy::disallowed_methods` along with `clippy::allow_attributes` and
  `clippy::allow_attributes_without_reason` so a bare `#[allow]` cannot silence
  it, and four tests guard the result: one over the configuration's shape, one
  that runs Clippy over a fixture calling all six methods, one that rejects the
  module-level `allow` that Clippy's own check misses, and one that keeps CI
  running them. The two reads the lint reports are test composition roots, each
  carrying an item-scoped `expect` with its reason: the child half of the
  re-exec'd write-failure test, and the idempotence harness's `PROPTEST_CASES`
  reader, which is injected so its fallback is exercised without a read.
  Recorded in `docs/adrs/0012-environment-seam-taxonomy.md`.
  ([#441]https://github.com/leynos/mdtablefix/issues/441)
- Release assets for `x86_64-apple-darwin`, `aarch64-apple-darwin` and
  `x86_64-pc-windows-msvc`, so `cargo binstall` can install `mdtablefix` on
  macOS and Windows without compiling it.
  ([#459]https://github.com/leynos/mdtablefix/issues/459)
- A `cargo-binstall` archive for `x86_64-unknown-freebsd`, which previously
  published only a bare binary. Every target the ungated `pkg-url` template
  resolves to now has an asset behind it.
- `scripts/package_release_artifacts.py`, a standard-library packager that
  stages the bare binary, the `cargo-binstall` archive and their `.sha256`
  sidecars identically on Linux, macOS and Windows runners.
- `scripts/verify_binstall_layout.py` and `tests/release_packaging.rs`, which
  render `pkg-url` and `bin-dir` from `Cargo.toml` and fail unless the staged
  archive matches, for every published target.
- A `binstall-packaging` continuous-integration job that builds, stages,
  extracts and runs the release binary on Ubuntu, macOS and Windows.
- A `windows-atomic-contract` continuous-integration job that runs the atomic
  replacement suites, and then the whole test suite, on a Windows runner, so a
  change that is correct only on Unix is caught by the pull request rather than
  by the release that follows it.
  ([#465]https://github.com/leynos/mdtablefix/issues/465)
- `--code-emphasis` flag to fix emphasis markers that adjoin inline code.
  Runs before wrapping and footnote conversion.
- Treat common English date sequences as atomic inline fragments during
  wrapping, including ordinal day, numeric day, and month-name-first forms.
  ([#346]https://github.com/leynos/mdtablefix/issues/346)
- The in-place replacement path emits bounded `metrics` counters and a
  `mdtablefix_io_replace_duration_seconds` histogram, so a host application
  that installs a recorder can watch replacements, their outcomes, how long
  they take, how often a temporary name is already taken or the name space is
  exhausted, temporary files a failed replacement could not remove, and
  symbolic-link targets declined rather than replaced. The crate installs no
  recorder itself.
  ([#465]https://github.com/leynos/mdtablefix/issues/465, [#471]https://github.com/leynos/mdtablefix/issues/471)
- Add the line-ending helpers `LineEnding`, `LineEndingCounts`,
  `count_line_endings`, `detect_line_ending`, and `serialize_lines`, so a
  caller can select and apply the majority line-ending style of an input
  document through pure queries.
  ([#451]https://github.com/leynos/mdtablefix/issues/451)
- `--check` reports each file that would be reformatted, with the line delta
  the rewrite would make, and `--diff` prints a unified diff of the changes
  instead. Both are read-only, both exit `1` when a file would change, and
  neither writes anything.
  ([#452]https://github.com/leynos/mdtablefix/issues/452)
- The footnote pipeline is split into three stages —
  `convert_inline_footnotes`, `renumber_footnote_labels` and
  `convert_footnote_definitions` — so a caller can run the reference stages
  before its own layout and the definition fold after it. `convert_footnotes`
  composes them in that order.
  ([#493]https://github.com/leynos/mdtablefix/issues/493)

### Changed

- `--in-place`, `rewrite` and `rewrite_no_wrap` replace a read-only file in a
  writable directory instead of failing, because the atomic swap needs write
  permission on the containing directory rather than on the file itself, and
  the replacement inherits the target's permissions, read-only included. On
  Windows the destination's `FILE_ATTRIBUTE_READONLY` blocks the rename, so it
  is cleared immediately before the swap and put back if the swap does not
  complete. ([#465]https://github.com/leynos/mdtablefix/issues/465)
- `--in-place`, `rewrite` and `rewrite_no_wrap` decline a symbolic link instead
  of replacing the link entry with a regular file, which previously left the
  real file untouched while destroying the link. Rewrite the link's target
  directly. ([#465]https://github.com/leynos/mdtablefix/issues/465)
- A failed file reports the full error chain, so a declined rewrite states its
  reason rather than only the file being written. See
  [Migrating to 0.6.0]docs/v0-6-0-migration-guide.md for the actions these
  changes require.
- Ungate the `[package.metadata.binstall]` configuration, which no longer
  applies only to 64-bit Linux GNU targets. One `pkg-url` template now covers
  Linux, macOS and Windows.
  ([#459]https://github.com/leynos/mdtablefix/issues/459)
- Checksum sidecars now name the asset alone rather than its staging path, so
  `sha256sum --check` works beside the downloaded file.
- Require callers of `FenceTracker::observe` and `FenceTracker::in_fence` to
  provide the current blockquote depth. This is a breaking API change for
  existing one-argument callers.
- Declare LF line endings for every tracked file in `.gitattributes`, so a
  Windows checkout hands the suites the fixture and snapshot bytes a Unix
  checkout sees rather than the CRLF that Git for Windows writes by default.
- Record the CLI matrix exit status as the numeric exit code rather than through
  `ExitStatus`'s `Display`, which spells an ordinary exit `exit status: 0` on
  Unix and `exit code: 0` on Windows. Envelopes now read `status: code: 0`,
  `status: code: <n>` for a non-zero exit, or `status: no exit code` when a
  signal ended the process, so the snapshots are platform-independent.
- Gate the `check-static-regexes` regression tests to Unix, because the guard
  they drive is a `bash` script that stands in for ripgrep with stub scripts
  carrying the executable bit. The Linux lint job still runs that guard over
  the same sources. ([#347]https://github.com/leynos/mdtablefix/issues/347)
- `format_breaks` now returns `Vec<Cow<'_, str>>` rather than `Vec<String>`,
  so unchanged lines stay borrowed instead of forcing heap allocations.
- Reserve exit `1` for drift and exit `2` for operational failure. A run that
  could not read or rewrite a file now exits `2` where it previously exited
  `1`, so a caller written as `mdtablefix ...; [ $? -eq 1 ]` needs updating.
  Drift fails a run only under `--check` and `--diff`, and an incomplete
  analysis is never reported as a clean tree. This is a breaking change for
  scripts, and the crate version becomes `0.6.0`.
  ([#451]https://github.com/leynos/mdtablefix/issues/451)
- Write only the files whose bytes would change under `--in-place`. A file that
  is already formatted keeps its inode and its modification time, and a
  symbolic link to such a file succeeds because no write is attempted.
- Renumber footnote labels before the wrap measures the line. A reference that
  shares its number with a trailing list item now promotes that item to a
  definition header before the wrap runs, so `--wrap --footnotes` indents the
  continuation of such a definition to seven columns rather than four. Both
  spellings are fixed points; the new indent matches what the formatter already
  emitted for the same definition written out directly.

### Fixed

- Preserve arbitrary Unicode table-cell payloads by carrying escaped-pipe and
  leading-empty parser state out of band. U+001D and U+001F no longer collide
  with legitimate cell content.
  ([#482]https://github.com/leynos/mdtablefix/issues/482)

- Emit formatted output with the line-ending style that holds the majority of
  the input's line endings, so a carriage return and line feed (CRLF) document
  is no longer rewritten as LF. A consistently ended document keeps its ending,
  while a mixed-ending document is normalized to the majority style. An exact
  tie, and a non-empty input with no line endings at all, select LF. An
  `--in-place` run over a CRLF file now writes CRLF rather than line feeds, and
  the change is not reversible for a file an earlier version already rewrote:
  its original endings are no longer recoverable from the file.
  ([#451]https://github.com/leynos/mdtablefix/issues/451)
- Format text adjoining an inline code span when it ends with a non-ASCII
  character, such as the ellipsis `--ellipsis` produces, instead of aborting on
  a character boundary. The emphasis split stepped one byte past the last
  character rather than one character past it.
- Write in-place output through a temporary file in the same directory and
  rename it over the target, so an interrupted run or a full disk can no longer
  leave a Markdown file truncated with no way to recover it. The original file
  mode is preserved, and a stale temporary file left by an abruptly killed run
  is retried past rather than reused.
  ([#465]https://github.com/leynos/mdtablefix/issues/465)
- Replace a read-only destination on Windows too, by clearing
  `FILE_ATTRIBUTE_READONLY` on the target immediately before the rename and
  putting the original attribute back if the swap does not complete, so a
  read-only file in a writable directory is replaced instead of failing. The
  temporary file carries the target's permissions into the rename, so the
  replacement is read-only as well.
  ([#465]https://github.com/leynos/mdtablefix/issues/465)
- Set the `cargo-binstall` `bin-dir` to `{ bin }{ binary-ext }`. The previous
  `.` rendered an empty source path, so `cargo binstall mdtablefix` failed
  before downloading anything.
  ([#458]https://github.com/leynos/mdtablefix/issues/458)
- Stop appending a trailing `|` to lone pipe-prefixed lines in code blocks, so
  shell pipeline continuations such as `| tee /tmp/test.log` are no longer
  corrupted into unterminated pipelines. A same-marker line carrying an info
  string is now treated as literal content rather than a closing fence, per
  CommonMark. ([#373]https://github.com/leynos/mdtablefix/issues/373)
- Reflow the prose following a joined cross-line inline-code span in the same
  `--wrap` pass, so formatter output is immediately idempotent.
  ([#375]https://github.com/leynos/mdtablefix/issues/375)
- Preserve authored line boundaries inside a cross-line inline-code span when
  joining it would exceed the wrap width and every authored source line already
  fits within that width, preventing new MD013 violations.
  ([#370]https://github.com/leynos/mdtablefix/issues/370)
- Document `--wrap` as a parameterless 80-column flag.
  ([#388]https://github.com/leynos/mdtablefix/issues/388)
- Keep reference-style links atomic while wrapping, so an opening bracket cannot
  be stranded before its label.
  ([#374]https://github.com/leynos/mdtablefix/issues/374)
- Keep bare bracket references atomic while wrapping, so an opening bracket
  cannot be stranded before its digits. The opener and the reference are
  recognized across the token boundary the segmenter emits and coupled into one
  span, so a second pass no longer rejoins the halves as `[ 1]`.
  ([#504]https://github.com/leynos/mdtablefix/issues/504)
- Keep colon-suffixed footnote references attached to preceding prose so they
  cannot become column-one footnote definitions.
  ([#372]https://github.com/leynos/mdtablefix/issues/372)
- Represent table row boundaries structurally so literal `ROW_END` cell content
  cannot corrupt reflow.
  ([#364]https://github.com/leynos/mdtablefix/issues/364)
- Preserve four-space and tab-indented code blocks when `--ellipsis` is used,
  so literal `...` in command output and source examples remains unchanged.
  ([#369]https://github.com/leynos/mdtablefix/issues/369)
- Preserve semantic `...` sequences in links, URLs, filesystem paths, and split
  link reference continuations when `--ellipsis` is used, preventing
  typographic normalization from changing resource destinations.
  ([#386]https://github.com/leynos/mdtablefix/issues/386)
- Keep hyphenated compounds containing an inline code span atomic during
  wrapping, including the leading-hyphen forms such as `` pre-`LLMPort` `` and
  `` (API-`Foo`) ``. The hyphen-prefix token is coupled forward to the
  following code span, mirroring the existing opening-punctuation behaviour.
  ([#307]https://github.com/leynos/mdtablefix/issues/307)
- Keep GFM footnote references coupled to sentence-ending punctuation on inline
  code spans and Markdown links during wrapping, including when the span is
  preceded by opening punctuation.
  ([#299]https://github.com/leynos/mdtablefix/issues/299)
- Keep opening brackets attached to following inline code spans and links during
  wrapping instead of stranding punctuation at line ends. (
  [#293]https://github.com/leynos/mdtablefix/issues/293)
- Keep inflectional affixes (`s`, `'s`, `ed`, `ing`) and hyphenated compounds
  attached to the preceding inline code span during paragraph reflow. A suffix
  that directly follows a closing backtick fence is absorbed into the code
  token, so the two never end up on separate lines after wrapping.
  ([`#300`]https://github.com/leynos/mdtablefix/issues/300)
- Preserve inline GFM footnote references as unbreakable tokens when wrapping
  Markdown paragraphs. ([#277]https://github.com/leynos/mdtablefix/issues/277)
- Preserve link reference definitions verbatim when `--wrap` is used, so
  labels, URLs, and optional titles are never collapsed into prose or split
  across lines. ([`#292`]https://github.com/leynos/mdtablefix/issues/292)
- Normalize whitespace-only artefacts during wrapping by rebalancing atomic
  tails.
- Preserve trailing spaces on the final line when wrapping Markdown, retaining
  hard break semantics. See [trailing spaces]docs/trailing-spaces.md for
  details. ([#65]https://github.com/leynos/mdtablefix/issues/65)
- Preserve fenced and indented code blocks verbatim when `--wrap` is used, so
  commands inside code examples are not joined or re-wrapped. (
  [#261]https://github.com/leynos/mdtablefix/issues/261)
- Keep trailing punctuation attached to Markdown links and inline code spans
  during wrapping to maintain readability.
- Allow wrapping between space-separated inline code spans instead of treating
  the full sequence as a single unbreakable unit. (
  [#252]https://github.com/leynos/mdtablefix/issues/252)
- Avoid converting numeric references in ATX heading text (including headings in
  blockquotes and list items) when the `--footnotes` option is enabled.
- Compute continuation-line indentation from Unicode display width (via
  `UnicodeWidthStr::width`) rather than byte or character count, so prefixes
  containing full-width characters no longer misalign wrapped output.
- Convert `<table>...</table>` blocks that span multiple lines and carry
  leading indentation, leaving surrounding non-table lines at the same
  indentation level untouched.
- Preserve a document's line-ending style and byte-order mark when rewriting
  files. A CRLF file keeps CRLF instead of being converted to line feeds, and a
  byte-order-marked table is now detected and reflowed rather than leaving its
  header verbatim with the data row before the separator. The bytes written
  change, so a file already processed by an earlier version is rewritten once
  more, and that rewrite cannot be undone by reverting this change.
  ([#451]https://github.com/leynos/mdtablefix/issues/451)