# Post-v1.3.0 polish audit

**Date:** 2026-05-06
**HEAD:** `8d0ed1f` (v1.3.0, just released to crates.io)
**Scope:** decide whether v1.3.1 patch is warranted.

---

## Verdict

**v1.3.1 patch warranted — 5 items below, one is a silent breaking trait regression introduced in v1.2.0.**

---

## Findings

### 1. `Console: !Sync` regression (BLOCKER)

**Severity:** blocker. **Introduced:** v1.2.0. **Surfaces in:** v1.2.0 and v1.3.0.

`src/console.rs:305` declares
```rust
pub(crate) writer_override: Option<Box<dyn std::io::Write + Send>>,
```
The trait object is `Send` only, no `+ Sync`. As a result, `Console` is no longer `Sync`. Verified empirically against published crates.io artifacts:

- `gilt = "=1.1.0"` → `assert_sync::<Console>()` compiles.
- `gilt = "=1.2.0"` → `assert_sync::<Console>()` fails (`Option<Box<dyn Write + Send>>` is `!Sync`).
- v1.3.0 (this HEAD) → same failure.

This is a silent SemVer-breaking trait change shipped without a CHANGELOG note. CHANGELOG.md:49 documents `with_writer` but does not mention the `Sync` loss.

**Suggested fix (v1.3.1):** change to `Option<Box<dyn std::io::Write + Send + Sync>>` and update the bound on `with_writer<W: Write + Send + Sync + 'static>`. `Vec<u8>`, `File`, `Stdout`, and most reasonable writers are already `Sync`. If a user genuinely needs a `!Sync` writer, they can wrap in `Mutex`. Add a CHANGELOG entry under v1.3.1.

### 2. Stale version refs in `src/lib.rs` doc comments (should-fix)

These render on docs.rs *right now* and tell new users the wrong Cargo.toml.

- `src/lib.rs:16`: `//! gilt = "0.10"` → `"1.3"`.
- `src/lib.rs:573`: `//! gilt = { version = "0.10", default-features = false }` → `"1.3"`.

`src/lib.rs:326` (`"version": "0.6.0"`) is a fake JSON value inside an example, not a stale ref. README.md:17 and crates/gilt-derive/README.md:15 are already correct (`"1.3"`).

### 3. Zero tests + zero examples for `Console::with_writer<W>` (should-fix)

The v1.2.0 headline addition has no test coverage and no example.

- `grep -rn 'with_writer\|writer_override' src/ tests/ examples/` → only the implementation sites in `src/console.rs:305,439-440` and `src/console_render.rs:534,590`. No assertion that a `Vec<u8>` writer captures the expected bytes.
- `src/console_tests.rs` (1624 lines) contains zero matches for `with_writer`.

**Suggested fix:** add one test in `src/console_tests.rs` that does `Console::default().with_writer(Vec::<u8>::new())`, prints, downcasts/recovers the buffer, and asserts the output. (Recovering the buffer requires either `Box<dyn Write + Send + Sync + Any>` or a small accessor; if not desired, at minimum a test that confirms `print` doesn't panic with a `Vec<u8>` writer and that `record=true` capture still works alongside the override.) This pairs naturally with finding #1.

### 4. Example warnings (nit)

`cargo build --features derive --examples` produces 3 warnings:

- `examples/protocol.rs:128` — `variant Critical is never constructed`.
- `examples/track_demo.rs:165` — `unused variable: b` (rename to `_b`).
- `examples/save_table_svg.rs:11` — `unused import: gilt::style::Style`.

Trivial fixes; folds into v1.3.1 as cleanup.

### 5. `MIGRATION_v1.md` is stale (nit)

`MIGRATION_v1.md:1` titled "Migrating to gilt v1.0", and the closing section (lines 396-402) still says "Future phases" listing Phase 6/6.5/7 against issue #20. v1.1.0, v1.2.0, and v1.3.0 have shipped since. Either:

- Append a short "v1.0 → v1.x: no migrations required" note (the v1.1/v1.2/v1.3 surfaces are additive), **or**
- Rename the doc to `MIGRATION_v1.0.md` and add an empty `MIGRATION_v1.x.md` placeholder so docs.rs / GitHub readers don't think v1.0 is still in progress.

---

## False alarms (verified, no action needed)

- **TODO/FIXME/XXX/HACK in `src/` and `crates/gilt-derive/src/`:** zero hits.
- **Tests left behind in pre-split files** (`console.rs`, `style.rs`, `prompt.rs`, `markdown.rs`, `utils/pretty.rs`, `error/traceback.rs`): all 0, except one intentional `#[test]` block in `src/console.rs:1115-1118` which is the `#[path = "console_tests.rs"]` mod attachment, not a stranded test.
- **Method-name collisions across split `impl Console` blocks:** apparent duplicates of `width`, `new`, `height`, `color_system` are on `ConsoleBuilder` (in `src/console_builder.rs`) vs `Console` (in `src/console.rs`). No collision.
- **`pub(crate)` leaks** in console_*.rs: all reasonable (`writer_override` field, `from_builder` constructor, `write_segments` helper, builder-internal fields).
- **rustdoc broken intra-doc links:** `RUSTDOCFLAGS="-D rustdoc::broken_intra_doc_links" cargo doc --no-deps --features derive` is clean.
- **CHANGELOG stale "deferred to v1.x" / "future phase":** zero hits.
- **Release build:** `cargo build --release --features derive --examples` succeeds with the same 3 warnings above; no errors.
- **Examples runtime smoke** (badge, breadcrumbs, panel, figlet, styles, cookbook_dashboard, from_conversions, raii_guards): all exit 0. `showcase` hit the 3s timeout — by design (`thread::sleep(Duration::from_millis(500))` on `examples/showcase.rs:566,570`), not a bug.
- **`cargo test --features derive --lib`:** 2171 passed, 0 failed.
- **`Console: Send`:** confirmed.

---

## Recommended v1.3.1 scope

Items #1, #2, #3, #4 in one patch release. Item #5 is doc-only and can ride along.

The Sync regression (#1) is the strongest reason to ship v1.3.1 sooner rather than later — it is a silent trait change that landed without a CHANGELOG note, and downstream code that previously assumed `&Console: Send + Sync` is now broken in confusing ways.
