Releases: ddsha441981/wiredoctor
Release list
v1.1.4 — Honest media
The README hero and every screenshot in the report tour were captured on 0.7.1 — four releases of UI ago. The front door was showing a product that no longer existed. This release recaptures all of it from a real 1.1.4 run, and recapturing it turned up two genuine bugs, both fixed here.
No API, schema, or configuration changes. Drop-in upgrade from 1.1.x.
Fixed
The startup-time trend chart was empty on diff runs. trendHistory was only put on the report inside the baseline-write=true branch, so the run you actually do day to day — diff mode, gates armed — rendered "Need at least 2 baseline writes" even with a full history committed in your baseline. The history is now handed to the report in diff mode too. The current run is deliberately not appended: it is not a baseline entry, and putting an unrecorded point on a committed trend would be a lie the chart cannot take back.
Every generated report was stamped dev. The header chip reads its version from the jar manifest, and the build never wrote an Implementation-Version into it — so every report anyone has ever generated said dev instead of the release that produced it. Fixed in the build, for everyone, not just for the screenshots.
Changed
All report media recaptured from a real 1.1.4 run. A start.spring.io app on Boot 4.1.x — 429 beans, 436 wiring edges — carrying a genuine setter-injected dependency cycle, a slow @PostConstruct bean, an untouched exporter, first-touch ghost tracking enabled, and three baseline writes behind the trend chart. Seven screenshots plus three GIFs: the full tour, a graph drill-down, and the timing charts.
The report tour is rewritten against that run. Every number, bean name and verdict in the prose now comes from the report the screenshots show, and the "captured on 0.7.1" disclaimer is gone.
sample/v1.1.4/ holds the complete matching set — report, JSON, baseline, diff, ghost report and gate status — and is what the live open this report links point at. The 0.10.0 report stays in sample/test2/ for comparison.
Added
tools/capture-media.js drives a real generated report in a headless browser and writes exactly the files the docs embed — so the media cannot quietly drift from what the report renders again.
Install
<dependency>
<groupId>io.github.ddsha441981</groupId>
<artifactId>wiredoctor-autoconfigure</artifactId>
<version>1.1.4</version>
</dependency>Note: 1.1.4 is not on Maven Central yet — this is the tagged GitHub release. Until it is published,
1.1.0is the version that resolves.
Tests: 261 in wiredoctor-autoconfigure + 11 in wiredoctor-actuator, green across Boot 2.7 / 3.3 / 3.5 / 4.0 on Java 17, 21 and 25.
Full changelog: 1.1.3...v1.1.4
v1.1.0 — Thread Distribution & Startup Time Trend
Two new diagnostics, one architecture migration that fixes a real bug, and a UI pass on the HTML report. Purely additive — schemaVersion stays 1, no config property renamed, no new dependency.
🧵 Thread Distribution — did anything actually leave main?
spring.beans.instantiate startup steps now carry a threadName tag captured at step-creation time — the only point where the instantiating thread is still on the stack. Reading Thread.currentThread() at analysis time, as the old code did, always returned main.
The report gains a threadDistribution section mapping each thread to its beans plus a per-thread count. The Timing tab renders it as a donut chart and a table ordered by bean count.
"threadDistribution": {
"perThread": { "main": ["dataSource", "entityManagerFactory"], "app-bg-1": ["warmCacheBean"] },
"counts": { "main": 2, "app-bg-1": 1 }
}The honest framing matters here: Spring does not parallelise bean initialisation by default, and no Boot version has. Background init is opt-in per bean and needs both @Bean(bootstrap = Bean.Bootstrap.BACKGROUND) (Framework 6.2+ / Boot 3.4+) and a bootstrapExecutor. Without both, a 600-bean application legitimately reports 100% on main — the card shows a parallel init not active pill rather than implying something is wrong. The one case where this chart really earns its place: you marked a slow bean BACKGROUND and gave it an executor — did it actually move off main, or is it silently still blocking startup?
Validated on Spring Cloud Config Server (616 beans, Boot 4.1): 610 on main, 1 on configserver-bg-1 — the single @Bean(bootstrap = BACKGROUND).
📈 Startup Time Trend — catch creep before the gate trips
Every run with wiredoctor.baseline-write=true appends {timestamp, totalStartupMs, slowBeanCount} to trendHistory[] in wiredoctor-baseline.json, capped by wiredoctor.trend-history-size (default 30, 0 = unlimited). The Timing tab renders a sparkline, so a +50ms/week drift becomes visible long before the startup-time gate trips on a single bad run.
Worth knowing: the sparkline appears only in reports from a baseline-write=true run, and only from the second write onward — trendHistory[] lives in the baseline file, not in the per-run report. A nightly baseline-refresh job is the natural place to look.
📊 Report UI
Still plain HTML/CSS/JS in one self-contained file — no libraries added.
- Overview charts — a 5-bucket bean instantiation-time histogram (
0/1–9/10–49/50–99/≥100ms), a bean-role stacked bar (ROLE_APPLICATION/SUPPORT/INFRASTRUCTURE), and an autoconfiguration-outcome stacked bar (matched / unconditional / notMatched / excluded). Every column carries its exact count, so the slow tail — always the smallest bucket and always the one you care about — never renders as an invisible sliver. Palettes are colour-blind validated against the dark surface, and every series is legend-labelled so identity is never colour-alone. - Responsive sidebar — under 860px the sticky nav becomes an off-canvas drawer (hamburger, backdrop,
Escape, auto-close on tab select). KPIs, cards, and header chips reflow at 860px/560px instead of overflowing. Motion respectsprefers-reduced-motion. - Table polish — sticky uppercase headers, zebra rows, tabular-aligned numeric columns, hover row highlight with an accent edge marker, and horizontal scroll confined to the card instead of the page.
🔧 Changed
WireDoctorStartupListener(ApplicationListener) →WireDoctorEnvironmentPostProcessor(EnvironmentPostProcessor) +WireDoctorBufferingApplicationStartup(extendsBufferingApplicationStartup). The processor now runs before context preparation, soApplicationStartupis installed at the correct lifecycle phase; the subclass keeps Actuator/actuator/startuptype checks intact.beanThreadMapis nowMap<String, Set<String>>, so a prototype instantiated on several threads keeps all of them instead of overwriting.
🐛 Fixed
- Politeness contract restored. The processor only replaces
ApplicationStartup.DEFAULTornull. A plainBufferingApplicationStartupis transparently upgraded (same type, extra thread tagging); a foreign implementation is never overwritten —threadDistributionis omitted with a warning and every other analysis still runs. - Compat fix: a new test used
JsonNode.values(), which only exists on the Jackson shipped with Boot 3.5+/4.x. Now iteratesJsonNodedirectly, which works on Jackson 2.x and 3.x alike. - Report layout: fan-in counts no longer wrap one digit per line; two-column rows no longer stretch the shorter card.
- Docs: five false claims removed from
docs/thread-distribution.md(including a non-existent donut hover interaction and inventedtask-1/task-2thread names);wiredoctor.trend.history-sizecorrected towiredoctor.trend-history-size.
✅ Verification
- 253 tests green — 242 autoconfigure + 11 actuator, 0 failures. 7 new tests cover trend-history carry-forward and capping, thread tagging, and the EnvironmentPostProcessor politeness paths.
- Compatibility matrix green across Boot 2.7.18 / 3.3.13 / 3.5.15 / 4.0.1 on Java 17 / 21 / 25 (12 combinations).
- Report UI checked at 1440px / 430px / 390px on every tab.
📦 Install
<dependency>
<groupId>io.github.ddsha441981</groupId>
<artifactId>wiredoctor-spring-boot-starter</artifactId>
<version>1.1.0</version>
</dependency>📚 Documentation
Full docs are live at https://ddsha441981.github.io/wiredoctor/ — including the two new v1.1.0 guides, Thread Distribution and Startup Time Trend.
A set of links on the docs site was broken at the moment this release was published and was fixed immediately after in #42: doc-to-doc links pointed at .md, which serves raw Markdown source instead of the rendered page, and the sample-report link 404'd because sample/ lives at the repo root while Pages publishes only docs/. The start.spring.io sample report is now published and browsable: open it live.
Full changelog: v1.0.0...v1.1.0
v1.0.0 API Freeze & Certification
The promises become contracts. From this release, wiredoctor.* config property names, report JSON schema, and performance budget are frozen — patch/minor releases in 1.x carry no breaking changes.
Changes
-
📌
schemaVersion: 1as first field in everywiredoctor-report.json(frozen) -
🔒
CycleDetector+WireDoctorHtmlReportersealedfinal -
⚡ AOT/GraalVM native graceful skip via
NativeDetector.inNativeImage() -
⏱️ Performance budget test — 1k beans < 30s, asserts
schemaVersion: 1 -
📚
mkdocs.yml+docs/index.md— GitHub Pages foundation -
📋 Deprecation policy in
docs/configuration.md -
🔢 Version bump:
0.10.0→1.0.0across all poms
Test results
224 tests green, 0 failures.
v0.10.0 — Graph Timing
Graph Timing: WireDoctor's core value — startup timing analysis — is now visible directly in the Graph tab, not just the Timing tab.
Added
- 🔥 Timing heat chip — toggles node color (green→red, log scale) and size mapped to per-bean instantiation time, so expensive beans jump out at a glance. Off by default; cycle/ghost/proxy/orphan colors keep priority; untimed (never-instantiated) beans stay default.
- 🥇 Critical path chip — traces the startup critical path in gold through the graph (nodes glow, path edges widen) and dims everything else. Handles condensed
[cycle: a, b]path entries by highlighting all members. Hidden when no timing data is available. Composes with filters and heat. - 🔍 Inspector timing line — the node panel now shows
Instantiation: N msand anon critical pathtag where applicable. - 📊
beanTimingsJSON field — the full per-bean instantiation timing map is serialized in the report (previously onlyslowBeansabove the threshold survived).
Verification
- 234 tests green (223 autoconfigure + 11 actuator)
- Validated on a real start.spring.io run (390 beans, 386 timed) — refreshed sample report: https://ddsha441981.github.io/wiredoctor/sample/test2/wiredoctor-report.html
Full Changelog: v0.9.0...v0.10.0
v0.9.0
What's Changed
- Feat/v0.8.0 verification debt by @ddsha441981 in #21
- docs: v0.9.0 — Front Door (README rewrite) by @ddsha441981 in #22
Full Changelog: v0.8.0...v0.9.0
feat: v0.8.0 — Verification Debt
v0.8.0 — Verification Debt
Closes the original design plan's unfinished verification checks (§3) before the v1.0.0 API freeze turns their answers into contracts — plus a CI-flakiness fix found during real-world gate
validation.
🌊 WebFlux/reactive validation (plan §3 check #4 — never done until now)
- New
WireDoctorWebFluxIntegrationTestboots a REACTIVE (Netty, non-servlet) context in CI and asserts reports are written, startup timings populated, and ghost analysis runs - Reactive entry points added to ghost detection:
WebHandler,WebSocketHandler,WebExceptionHandler(joiningRouterFunction) — fixes ghost false-positives in reactive apps - Pinned by test: a
RouterFunctionbean is never flagged as a ghost spring-boot-starter-webfluxadded test-scope only; README supported-versions note added
🔬 Bean-scope behavior pinned by tests (plan §3 check #2 — doc-only until now)
- New
WireDoctorBeanScopeTestasserts the documented behavior per scope:- prototype definitions appear as graph nodes but are never instantiated (zero-intrusion promise, now regression-tested)
- untouched
@Lazybeans survive the full analysis untouched FactoryBeanproduct edges resolve to the factory's bean name- skipped not-instantiated beans are honestly counted (
proxies.notInstantiatedSkipped)
- README Limitation #5 rewritten from vague ("may not fully map out") to these test-backed facts
🤝 Never overwrite a user-set ApplicationStartup (found in plan audit)
- The startup listener previously replaced any
ApplicationStartupthe app (or another tool) had installed — silently breaking foreign instrumentation - Now: a pre-existing non-default instance is kept. Buffering ones keep timings working (INFO); foreign ones get a WARN and the timing section degrades gracefully while every other
analysis runs normally getApplicationStartup()verified available on Boot 2.7 → 4.0 (same floor asBufferingApplicationStartup)- Contract pinned by
WireDoctorStartupCoexistenceTest
⏱️ Slow-bean gate jitter margin — wiredoctor.slow-bean-margin-ms (default 20)
- Real-world validation on start.spring.io tripped the gate at 101ms vs a 100ms threshold — pure JVM jitter, not a regression. That exact case is now a permanent test
- A NEW slow bean must exceed
threshold + marginto trip; beans inside the margin band stay in the report's slow-bean list but never fail CI - Mirrors the dual-threshold noise tolerance the startup-time gate already has;
0restores exact pre-0.8.0 behavior - Margin surfaces in
gates.configand the HTML gate card ((+20ms jitter margin)) - Docs:
docs/performance-gates.md+ README config section updated
Verified
- 233 tests green (222 autoconfigure + 11 actuator) via full
mvn verify— up from 219 - ~60 lines production code, ~350 lines tests — code-light, proof-heavy by design
- Version bumped in all 4 poms; CHANGELOG entry added
Plan §3 checklist after this PR
| Check | Status |
|---|---|
| #1 SPI hook timing | ✅ resolved v0.1.0 + CI matrix |
| #2 Bean scopes | ✅ this PR |
| #3 AOT/native | → v1.0.0 (deliberate, task 3) |
| #4 WebFlux | ✅ this PR |
v0.7.1
v0.7.0
v0.6.1
What's Changed
- feat: HTML report redesigned as tabbed diagnostic console (v0.6.1) by @ddsha441981 in #18
Full Changelog: v0.6.0...v0.6.1
v0.6.0
What's Changed
- Feat/v0.6.0 ghost bean detector by @ddsha441981 in #17
Full Changelog: v0.5.0...v0.6.0