Skip to content

Package Versioning

Version-numbering policy for the eight publishable @atlcli/* packages. This governs packed-tarball and filesystem-linked artifacts only — publishing to a package registry is deliberately deferred (product rename pending; see the “Deferred: npm registry publishing” appendix in specs/export-expansion/009-package-publishing/PLAN.md), and no CI path in this repo can publish.

The @atlcli/* packages are developed and tested only together, on the existing release tooling (scripts/release.ts, git-cliff changelog, one tag) — there is no per-package release schedule. Since the API freeze there are two version tiers: the frozen set at 1.0.0 (confluence, docx, pdf, pdf-compiler-browser, export-macros) and the rest still at 0.x (see the freeze table). Internal @atlcli/* dependencies use workspace:*, which bun pm pack rewrites to each dependency’s concrete current version at pack time — so a packed tarball resolves its siblings to whatever version they carry at that commit (mixed 1.0.0/0.x today), not to a single train number.

What the tooling actually verifies: scripts/pack-check.test.ts asserts no workspace: range survives into a packed manifest and that every exports target exists in the tarball; the consumer-smoke suites additionally assert no workspace: leakage in the installed manifests and that the packages build and export end-to-end. Neither asserts a single uniform version across packages — that uniformity is a development-process convention, not a checked invariant.

While a package is 0.x:

  • Breaking changes bump the minor and must appear under a “Breaking” changelog heading.
  • Patch releases are strictly non-breaking.

The freeze (spec 009 T4.2) is executed — the export-expansion folders 001–008 landed and five packages froze at 1.0.0 with reviewed closure classifications (committed at packages/<p>/etc/<p>.closure.md, CI-guarded together with the api reports). Standard semver applies to frozen packages (breaking = major, with one @deprecated minor first); the pre-1.0 rules keep applying to the rest. A registry publish remains deferred either way.

Package Version Frozen Why
@atlcli/confluence 1.0.0 yes Core v1 seams: ExportBlock/storageToBlocks, ExportNoteCode registry, client, TreeSource/fetchExportTree/composeChapters
@atlcli/docx 1.0.0 yes ExportEnv/runExport seams, proven across three hosts; internals behind ./scan/./internal
@atlcli/pdf 1.0.0 yes PdfExportEnv/runPdfExport + PdfCompilePort contract; internals behind ./internal
@atlcli/pdf-compiler-browser 1.0.0 yes Tiny stable surface over the sha256-pinned vendored compiler
@atlcli/export-macros 1.0.0 yes MacroRendererRegistry/resolveMacroBlocks contract is embedded in the frozen docx/pdf surfaces; the renderer set may grow additively
@atlcli/core 0.6.0 no Barrel is largely CLI/Bun-internal; frozen packages freeze only their use of its types (frozen-by-closure)
@atlcli/diagram 0.6.0 no Renderer-internal beyond renderDiagram/DiagramTheme (frozen-by-closure via docx)
@atlcli/jira 0.6.0 no Never API-reviewed; Bun-only engines
@atlcli/plugin-api 0.6.0 no Never API-reviewed
@atlcli/template-pack 0.6.0 no Spec 007 did not decide public-API status; the byte format has its own manifest versioning
@atlcli/export-node 0.6.0 no Days old; convenience surface should harden against real consumers first
@atlcli/export-wiring 0.1.0 no Created in spec 010 by promoting the CLI’s host-wiring module so the extension stops carrying a second copy; its shape still follows the hosts
  • Removed or renamed exports from a published entrypoint
  • Narrowed input types or widened output types of the frozen seams
  • Changed or removed exports subpaths (including asset subpaths — see the Export Asset Contract)
  • Changed asset filenames (fonts, wasm)
  • Tightened engines (dropping a supported runtime)

Deprecations get one minor release with @deprecated JSDoc before removal.

Changes that break a frozen v1 seam without the usual @deprecated-minor cycle are recorded here, with the reasoning, so the exception stays visible instead of being rediscovered from a diff. The cycle is skippable today for exactly one reason: nothing is published, so a frozen package has no installed base to migrate — there are no consumers outside this repo and its consumer-smoke matrix. Once a registry publish happens, this list closes and the normal policy applies without exception.

PdfOutputSink.emit takes a PdfBytesHandle (spec 010)

Section titled “PdfOutputSink.emit takes a PdfBytesHandle (spec 010)”

@atlcli/pdfemit(name, bytes, context?)’s second parameter changed from Uint8Array to PdfBytesHandle.

  • Why. Every layer that wanted a different shape of the finished document built its own copy of it. The extension’s download path held new Blob([bytes]) while the caller’s Uint8Array was still reachable — a measured +64.0 MiB for a 64 MiB PDF, both copies live at once. A handle owns one representation and converts on demand, so the same document is never materialized twice in two shapes.
  • Migration. A sink that wants bytes calls await bytes.asUint8Array(); for the default array-backed handle that is the compiler’s own buffer, not a copy. A sink that wants a Blob or an object URL asks for one directly instead of constructing it.
  • Why no deprecation minor. An overload accepting both shapes would have kept every consumer’s new Blob([...]) path — the exact allocation the change removes — alive and type-legal for a release, in exchange for migrating an installed base of zero.
  • Guarded by. The consumer-smoke suites (scripts/consumer-smoke.ts, scripts/consumer-smoke-vite.ts) assert the handle contract from an external consumer’s position against the packed tarballs — both the runtime shape and, under moduleResolution: NodeNext with skipLibCheck: false, that a consumer can name PdfBytesHandle and type a sink against it.

There is one CHANGELOG.md, generated from Conventional Commits. The commit scope is the per-package story: feat(confluence): …, fix(pdf): …, feat(docx)!: … — filtering the changelog by scope yields each package’s history without a second changelog system.

Considered and rejected (decision recorded here so it isn’t re-litigated blindly):

  • The repo already runs a single-version release train with one changelog and one tag; changesets would add a second changelog system and a bot workflow sized for many independent maintainers, which this repo doesn’t have.
  • Per-package version drift would force consumers (and the consumer-smoke matrix) to reason about version compatibility between sibling packages that are developed and tested only in lockstep.
  • Conventional-Commit scopes already deliver the per-package changelog story.

This reasoning is independent of the publish target, so it holds while registry publishing is deferred. Consequently there is no .changeset/ directory in this repo.

apps/cli (@atlcli/cli) is not part of the publishable set: it stays this repo’s own CLI entry point (private: true), bundled by its existing apps/cli/build.ts into the repo-root dist/ for the GitHub-release binaries. It has no external package-consumption story; the earlier npm install -g @atlcli/cli path is retired with the registry deferral.

Jira and Confluence are trademarks of Atlassian Corporation Plc. atlcli is not affiliated with, endorsed by, or sponsored by Atlassian.