From 0c554e3f6ec4bfb1900ba0dc47f8dc28bf7c8f9f Mon Sep 17 00:00:00 2001 From: Harnoor Lal Date: Sun, 22 Feb 2026 16:18:40 -0800 Subject: [PATCH] docs: add changelog covering all releases Add CHANGELOG.md following Keep a Changelog format, covering v0.1.0 through v0.4.0 with linked issue/PR references for all entries. Update docs/standards/releases.md: replace "No CHANGELOG.md" stance with the new convention and add changelog step to release checklist. Closes #133 --- CHANGELOG.md | 268 +++++++++++++++++++++++++++++++++++++ docs/standards/releases.md | 24 ++-- 2 files changed, 281 insertions(+), 11 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9a52668 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,268 @@ +# Changelog + +All notable changes to chainsaw are documented here. Each release corresponds to a +[milestone](https://github.com/rocketman-code/chainsaw/milestones?state=closed) on GitHub. + +## [0.4.0] - 2026-02-22 + +Structured output layer, REPL polish, file watching. + +### Added + +- Structured report types (`TraceReport`, `ChainReport`, `CutReport`, `DiffReport`, `PackagesReport`) with `--json` output for all commands ([#103]) +- VFS abstraction layer: `Vfs` trait with `OsVfs` (zero-overhead pass-through), `GitTreeVfs` (gix-backed in-process git reading), and `OxcVfsAdapter` (oxc_resolver bridge) ([#113]) +- In-process git tree reading for `diff`, replacing temporary worktree checkouts ([#113]) +- REPL inline flags: `--json`, `--include-dynamic`, `--top`, `--top-modules`, `--ignore` on any command ([#99], [#124]) +- REPL session settings via `set`/`unset`/`show` commands for persistent flag defaults ([#124]) +- notify-based file watching with dirty flag and auto-refresh on REPL commands ([#98], [#117]) +- Query result caching for instant REPL repeat commands ([#105], [#120]) +- Pre-sorted tab completion candidates for binary search lookup ([#100], [#118]) +- Project standards directory at `docs/standards/` ([#119]) +- `cargo xtask bench` and `cargo xtask find-entry` subcommands ([#128]) +- 154 integration tests closing dispatch-layer blind spot ([#169]) + +### Fixed + +- `--chain`/`--cut` returned false "not reachable" on high-fanout modules due to backtracking discarding all paths ([#172], [#174]) +- Side-effect imports (`import "./foo"`) silently dropped by parser ([#171], [#173]) +- Diff cache corruption when working tree reads cache written by git-ref side ([#142]) +- `PackageListEntry` JSON field names inconsistent with `PackageEntry` ([#149]) +- False `direct_import: true` for nonexistent `--cut` targets ([#151]) +- Annotated tags cause "not a commit" error in diff ([#145]) +- Branch names containing slashes misclassified as file paths ([#146]) +- `--max-weight` threshold error printed twice ([#148]) +- `--diff-from` silently ignored `--json` flag ([#150]) +- REPL: unknown inline flags silently became positional arguments ([#144]) +- `cargo xtask perf-validate` failed in git worktrees ([#170]) + +### Changed + +- Replaced bash scripts with thin xtask aliases in Justfile ([#127], [#128]) +- Synthetic benchmark corpus augmented with pnpm, workspaces, and barrel files ([#125], [#129]) + +## [0.3.0] - 2026-02-21 + +Session abstraction, interactive REPL, contributor onboarding, CI. + +### Added + +- Interactive REPL mode (`chainsaw repl`) with commands: trace, chain, cut, diff, packages, imports, importers, entry, info, help ([#39], [#97]) +- Tab completion via rustyline for module paths and package names ([#97]) +- `Session` struct with stateful query API wrapping graph, cache, and entry resolution ([#97]) +- GitHub Actions CI: fmt, clippy, test on Linux/macOS/Windows ([#81], [#89]) +- `cargo xtask check` for local CI (fmt + clippy + test) ([#86]) +- Pre-commit hook running `cargo xtask check` on feature branches ([#87]) +- Contributor guide, issue templates, and PR template ([#84]) + +### Fixed + +- Cache hit suppressed per-file unresolvable dynamic import details ([#80], [#94]) +- Numeric flag validation: reject non-numeric values for `--top`, `--top-modules`, `--limit` ([#88]) +- `--chain`/`--cut` not-found messages written to stdout instead of stderr ([#88]) +- Misleading error messages for unsupported file types and directory entries ([#88]) + +## [0.2.0] - 2026-02-20 + +Git ref diff: compare dependency weight across commits, branches, and snapshots. + +### Added + +- `diff` subcommand: compare dependency weight across git refs (`chainsaw diff main feature src/index.ts`) ([#106], [#82]) +- Auto-detection of diff arguments: file snapshot path vs git ref ([#82]) +- Git worktree lifecycle helpers for clean branch comparison ([#82]) +- Crate-level, module-level, and public API documentation ([#82]) +- `#[non_exhaustive]` on all public structs for forward compatibility ([#82]) + +### Changed + +- Crate renamed from `chainsaw` to `chainsaw-cli` for crates.io publishing ([#82]) + +## [0.1.0] - 2026-02-20 + +First publish. Dependency graph analysis for TypeScript/JavaScript and Python. + +### Added + +#### Language Support + +- TypeScript/JavaScript: OXC parser with full ES module support, dynamic `import()`, `require()` detection, and re-export traversal +- TypeScript resolver: `oxc_resolver` with pnpm strict mode, 3-tier virtual store fallback, and workspace package name resolution via DashMap with negative caching +- Monorepo support: workspace package detection by walking up to `package.json`, cross-package `--diff` comparison ([#14], [#15]) +- Python: tree-sitter parser with `import`, `from ... import`, relative imports, `TYPE_CHECKING` (TypeOnly), `importlib.import_module` (Dynamic), function-scoped imports (Dynamic) +- Python resolver: source roots (project root, `src/`, `lib/`, conftest.py-discovered), venv site-packages (`.pth` files, `pyvenv.cfg` fast path), namespace packages (PEP 420), C extension probing (`.so`/`.pyd`) +- Python: `sys.path` expression evaluator via tree-sitter for `conftest.py` and `sitecustomize.py` modifications +- Conformance test suite validated against CPython's `importlib.find_spec` +- `LanguageSupport` trait with `ProjectKind` auto-detection from file extension + +#### CLI + +- `trace` command: transitive import weight with exclusive weight via dominator tree, heavy dependency ranking ([#23], [#24]) +- `--chain `: show all import chains from entry to a package or file ([#1], [#2], [#3], [#4], [#34]) +- `--cut `: find optimal cut points to sever import chains ([#5], [#6], [#19]) +- `diff` subcommand: compare two saved snapshots side-by-side with added/removed/changed sections ([#26]) +- `packages` subcommand: list all third-party dependencies with module counts ([#38]) +- `--save ` and `--diff-from ` for before/after snapshot comparison ([#9]) +- `--ignore `: exclude packages from trace output ([#25]) +- `--top N` and `--top-modules N`: limit heavy dependency and module lists ([#11], [#12], [#65]) +- `--max-weight `: exit non-zero when weight exceeds limit for CI gating ([#58]) +- `--include-dynamic`: fold dynamic imports into weight calculations and chain traversal ([#13], [#29]) +- `--quiet` / `-q`: suppress informational output for scripting ([#50]) +- `--no-color` flag with `NO_COLOR` and `TERM=dumb` environment variable support ([#57]) +- `--json` output for trace command +- `--version` flag ([#37]) +- `completions` subcommand for bash/zsh/fish/powershell ([#59]) +- Color output with auto-detection via `IsTerminal` ([#57]) +- Unresolvable dynamic import warnings with file locations ([#22], [#68]) +- Dynamic import data captured in trace snapshots ([#67]) + +#### Caching + +- Three-tier cache: tier 1 (whole-graph with file mtime stat-only fast path), tier 1.5 (incremental: re-parse changed files, reuse graph if imports unchanged), tier 2 (per-file parse+resolve, survives single-file edits) +- Lockfile sentinels tracking package manager lockfiles for cache invalidation ([#27]) +- Background cache serialization and disk write via `std::thread::spawn` +- `CacheWriteHandle::join` for deterministic shutdown + +#### Performance + +- OXC parser replacing SWC for TypeScript/JavaScript parsing +- Concurrent discovery pipeline with lock-free `SegQueue` work queue + `DashSet` dedup + `rayon::scope` workers ([#76]) +- Fused parse+resolve in single parallel pass +- `Vec` / `Vec` BFS indexing replacing `HashMap`/`HashSet` for dense `ModuleId` ranges ([#72], [#74]) +- Single-pass parent recording replacing per-package BFS for shortest chains ([#70], [#71]) +- Background thread cache serialization +- Thread pool capped at 8 to reduce VFS contention on macOS +- Python: `pyvenv.cfg` fast path for site-packages discovery, deferred C extension probing, bounded conftest scanning + +#### Performance Infrastructure + +- Adaptive benchmark harness with Welch t-test and early stopping (replacing criterion) +- `stats/` workspace crate with noise-aware statistical primitives (trimmed mean, confidence intervals) +- `perf.toml` registry mapping source files to benchmarks with complete coverage enforcement +- Pre-push hook running `cargo xtask perf-validate` on performance-sensitive changes +- Synthetic benchmark corpus as default target +- Justfile for external tool orchestration (`just bench-cold`, `just bench-all`) + +### Fixed + +- Scoped npm packages (`@scope/name`) in `--chain` and `--cut` ([#45]) +- Cache false invalidation on pnpm workspaces ([#41], [#27]) +- Dynamic `import()` in expression position not detected ([#8]) +- `--diff` misclassifying shared packages as "only in" one entry point ([#7]) +- Stale caches from new files and newly-resolvable imports ([#18]) +- Workspace packages showing as absolute filesystem paths instead of package names ([#15]) +- Duplicate edges from symlinks resolving to same target ([#17]) +- `--chain`/`--cut` not traversing dynamic edges with `--include-dynamic` ([#13]) +- `--cut` showing duplicate entries and every intermediate as a cut point ([#5], [#6], [#64]) +- `--diff` comparing only top N packages instead of all reachable ([#7]) +- `--save` silently doing nothing when combined with query flags ([#9]) +- `--chain`/`--cut` exit 0 when target not found ([#46]) +- `--top 0` showing misleading message instead of hiding section ([#47]) +- Negative deltas missing minus sign in diff output ([#61]) +- Entry point names showing as filenames in diff subcommand ([#63]) +- Chain display showing bare filenames for pnpm store paths ([#53]) +- Singular/plural forms for all count labels ([#56]) +- Flag conflict validation: `--chain`/`--cut` with `--diff`, same entry point in `--diff` ([#10], [#51], [#54]) +- Python: function-scoped imports classified as Static instead of Dynamic +- Python: cross-root phantom resolution for regular packages +- Python: dotted import component-by-component resolution +- Python: C extension loader precedence matching CPython +- `--chain`/`--cut` when target is the entry point itself ([#69]) + +[0.4.0]: https://github.com/rocketman-code/chainsaw/compare/v0.3.0...v0.4.0 +[0.3.0]: https://github.com/rocketman-code/chainsaw/compare/v0.2.0...v0.3.0 +[0.2.0]: https://github.com/rocketman-code/chainsaw/compare/819d7f1...v0.2.0 +[0.1.0]: https://github.com/rocketman-code/chainsaw/commits/819d7f1 + +[#1]: https://github.com/rocketman-code/chainsaw/issues/1 +[#2]: https://github.com/rocketman-code/chainsaw/issues/2 +[#3]: https://github.com/rocketman-code/chainsaw/issues/3 +[#4]: https://github.com/rocketman-code/chainsaw/issues/4 +[#5]: https://github.com/rocketman-code/chainsaw/issues/5 +[#6]: https://github.com/rocketman-code/chainsaw/issues/6 +[#7]: https://github.com/rocketman-code/chainsaw/issues/7 +[#8]: https://github.com/rocketman-code/chainsaw/issues/8 +[#9]: https://github.com/rocketman-code/chainsaw/issues/9 +[#10]: https://github.com/rocketman-code/chainsaw/issues/10 +[#11]: https://github.com/rocketman-code/chainsaw/issues/11 +[#12]: https://github.com/rocketman-code/chainsaw/issues/12 +[#13]: https://github.com/rocketman-code/chainsaw/issues/13 +[#14]: https://github.com/rocketman-code/chainsaw/issues/14 +[#15]: https://github.com/rocketman-code/chainsaw/issues/15 +[#17]: https://github.com/rocketman-code/chainsaw/issues/17 +[#18]: https://github.com/rocketman-code/chainsaw/issues/18 +[#19]: https://github.com/rocketman-code/chainsaw/issues/19 +[#22]: https://github.com/rocketman-code/chainsaw/issues/22 +[#23]: https://github.com/rocketman-code/chainsaw/issues/23 +[#24]: https://github.com/rocketman-code/chainsaw/issues/24 +[#25]: https://github.com/rocketman-code/chainsaw/issues/25 +[#26]: https://github.com/rocketman-code/chainsaw/issues/26 +[#27]: https://github.com/rocketman-code/chainsaw/issues/27 +[#29]: https://github.com/rocketman-code/chainsaw/issues/29 +[#34]: https://github.com/rocketman-code/chainsaw/issues/34 +[#37]: https://github.com/rocketman-code/chainsaw/issues/37 +[#38]: https://github.com/rocketman-code/chainsaw/issues/38 +[#39]: https://github.com/rocketman-code/chainsaw/issues/39 +[#41]: https://github.com/rocketman-code/chainsaw/issues/41 +[#45]: https://github.com/rocketman-code/chainsaw/issues/45 +[#46]: https://github.com/rocketman-code/chainsaw/issues/46 +[#47]: https://github.com/rocketman-code/chainsaw/issues/47 +[#50]: https://github.com/rocketman-code/chainsaw/issues/50 +[#51]: https://github.com/rocketman-code/chainsaw/issues/51 +[#53]: https://github.com/rocketman-code/chainsaw/issues/53 +[#54]: https://github.com/rocketman-code/chainsaw/issues/54 +[#56]: https://github.com/rocketman-code/chainsaw/issues/56 +[#57]: https://github.com/rocketman-code/chainsaw/issues/57 +[#58]: https://github.com/rocketman-code/chainsaw/issues/58 +[#59]: https://github.com/rocketman-code/chainsaw/issues/59 +[#61]: https://github.com/rocketman-code/chainsaw/issues/61 +[#63]: https://github.com/rocketman-code/chainsaw/issues/63 +[#64]: https://github.com/rocketman-code/chainsaw/issues/64 +[#65]: https://github.com/rocketman-code/chainsaw/issues/65 +[#67]: https://github.com/rocketman-code/chainsaw/issues/67 +[#68]: https://github.com/rocketman-code/chainsaw/issues/68 +[#69]: https://github.com/rocketman-code/chainsaw/issues/69 +[#70]: https://github.com/rocketman-code/chainsaw/issues/70 +[#71]: https://github.com/rocketman-code/chainsaw/issues/71 +[#72]: https://github.com/rocketman-code/chainsaw/issues/72 +[#74]: https://github.com/rocketman-code/chainsaw/issues/74 +[#76]: https://github.com/rocketman-code/chainsaw/issues/76 +[#80]: https://github.com/rocketman-code/chainsaw/issues/80 +[#81]: https://github.com/rocketman-code/chainsaw/issues/81 +[#82]: https://github.com/rocketman-code/chainsaw/pull/82 +[#84]: https://github.com/rocketman-code/chainsaw/pull/84 +[#86]: https://github.com/rocketman-code/chainsaw/pull/86 +[#87]: https://github.com/rocketman-code/chainsaw/pull/87 +[#88]: https://github.com/rocketman-code/chainsaw/pull/88 +[#89]: https://github.com/rocketman-code/chainsaw/pull/89 +[#94]: https://github.com/rocketman-code/chainsaw/pull/94 +[#97]: https://github.com/rocketman-code/chainsaw/pull/97 +[#98]: https://github.com/rocketman-code/chainsaw/issues/98 +[#99]: https://github.com/rocketman-code/chainsaw/issues/99 +[#100]: https://github.com/rocketman-code/chainsaw/issues/100 +[#103]: https://github.com/rocketman-code/chainsaw/issues/103 +[#105]: https://github.com/rocketman-code/chainsaw/issues/105 +[#106]: https://github.com/rocketman-code/chainsaw/issues/106 +[#113]: https://github.com/rocketman-code/chainsaw/pull/113 +[#117]: https://github.com/rocketman-code/chainsaw/pull/117 +[#118]: https://github.com/rocketman-code/chainsaw/pull/118 +[#119]: https://github.com/rocketman-code/chainsaw/pull/119 +[#120]: https://github.com/rocketman-code/chainsaw/pull/120 +[#124]: https://github.com/rocketman-code/chainsaw/pull/124 +[#125]: https://github.com/rocketman-code/chainsaw/issues/125 +[#127]: https://github.com/rocketman-code/chainsaw/issues/127 +[#128]: https://github.com/rocketman-code/chainsaw/pull/128 +[#129]: https://github.com/rocketman-code/chainsaw/pull/129 +[#142]: https://github.com/rocketman-code/chainsaw/issues/142 +[#144]: https://github.com/rocketman-code/chainsaw/issues/144 +[#145]: https://github.com/rocketman-code/chainsaw/issues/145 +[#146]: https://github.com/rocketman-code/chainsaw/issues/146 +[#148]: https://github.com/rocketman-code/chainsaw/issues/148 +[#149]: https://github.com/rocketman-code/chainsaw/issues/149 +[#150]: https://github.com/rocketman-code/chainsaw/issues/150 +[#151]: https://github.com/rocketman-code/chainsaw/issues/151 +[#169]: https://github.com/rocketman-code/chainsaw/pull/169 +[#170]: https://github.com/rocketman-code/chainsaw/issues/170 +[#171]: https://github.com/rocketman-code/chainsaw/issues/171 +[#172]: https://github.com/rocketman-code/chainsaw/issues/172 +[#173]: https://github.com/rocketman-code/chainsaw/pull/173 +[#174]: https://github.com/rocketman-code/chainsaw/pull/174 diff --git a/docs/standards/releases.md b/docs/standards/releases.md index dd17eac..45ab0f4 100644 --- a/docs/standards/releases.md +++ b/docs/standards/releases.md @@ -29,16 +29,17 @@ Batch related changes, release when there's a meaningful set of user-facing chan ## Release Checklist 1. All milestone issues closed -2. `cargo test --workspace` passes -3. `cargo clippy --workspace --all-targets -- -D warnings` clean -4. `cargo publish --dry-run` clean -5. Perf validation passes locally (`cargo xtask perf-validate`) -6. Bump version in `Cargo.toml` + run `cargo generate-lockfile` -7. Commit: `chore(cargo): bump version to 0.x.y` -8. Tag: `git tag v0.x.y` -9. Push: `git push && git push --tags` -10. Publish: `cargo publish` -11. Close the milestone on GitHub +2. Update `CHANGELOG.md` with the new release entry +3. `cargo test --workspace` passes +4. `cargo clippy --workspace --all-targets -- -D warnings` clean +5. `cargo publish --dry-run` clean +6. Perf validation passes locally (`cargo xtask perf-validate`) +7. Bump version in `Cargo.toml` + run `cargo generate-lockfile` +8. Commit: `chore(cargo): bump version to 0.x.y` +9. Tag: `git tag v0.x.y` +10. Push: `git push && git push --tags` +11. Publish: `cargo publish` +12. Close the milestone on GitHub ## Git Tags @@ -46,4 +47,5 @@ Format: `v0.x.y` (e.g., `v0.1.0`, `v0.2.0`, `v0.3.0`). Tag the version bump comm ## Changelog -No CHANGELOG.md file. Commit messages use conventional commits. GitHub releases with auto-generated notes from tags are sufficient. +`CHANGELOG.md` in the repo root, following [Keep a Changelog](https://keepachangelog.com/) format. +Each release maps to a closed GitHub milestone. Update the changelog as part of the release checklist.