Known gaps
This page enumerates everything piot deliberately doesn't do or doesn't do yet, in one place. If you're evaluating piot for a migration, read this alongside Concepts before assuming a feature is present.
Gaps come in three flavours:
- Out of scope (permanent). A design commitment — piot will not grow this. Compose with another tool if you need it.
- Not yet shipped. Tracked as a GitHub issue; may land later.
- Documented behaviour you might mistake for a gap. piot does the thing, but the common expectation differs from piot's model.
Out of scope (permanent non-goals)
Reference: notes/design-commitments.md.
Build-side cross-compilation
piot does not run maturin build, napi build, cargo build, or any other compilation step. Those live in your workflow's build job.
Related: piot does accept a per-target runner hint via object-form targets entries ({ triple, runner }) and emits that runner into the build-matrix. But the build job that compiles your binaries is yours to write. See Configuration → Target entries.
Version computation from commit content
piot does not diff commits to infer semver bumps. The version comes from either (a) a release: patch|minor|major commit trailer on the merge, or (b) {name, version} passed directly by an upstream tool like release-please or release-plz. piot is polyglot because it delegates bump computation to language-agnostic trailer logic or to per-language tools that already solved this.
Standalone binary archive uploads to GitHub Releases
piot does not emit .tar.xz / .tar.gz / .zip installer archives on GitHub Releases (the curl | tar x install path). That's cargo-dist and goreleaser territory; compose with them. If your release needs both registry publishes and a curl-installable binary, run cargo-dist and piot from the same workflow, in parallel.
Shell hooks / plugin APIs
No pre_publish, no post_tag, no on_release. If you need to run custom code around publish, do it as workflow steps before or after the publish job. piot's config surface stays declarative.
Changelogs
Generation and update of CHANGELOG.md belongs to release-please or similar. piot doesn't touch changelogs.
Monorepo discovery
Packages are declared explicitly via [[package]] entries. piot does not walk the filesystem to discover packages. The same config shape works for single-package and monorepo layouts.
Auto tag-rollback on partial-publish failure
piot does not delete the git tag after a publish leg fails. crates.io is immutable — deletion is not a safe undo. piot's pre-publish completeness check is designed to catch the class of failure that would have motivated rollback; when a failure still happens mid-flight, the right response is to bump-and-republish, not to delete the tag.
Shared-tag layout (v{version} across every package)
piot tags each package independently as {name}-v{version}. If your existing project ships every package under a single shared v{version} tag, that layout is not supported and will not be — per-package tags let depends_on cascades fire independently when only a subset of packages changes.
Not yet shipped (tracked)
Status of specific asks. Check the linked issues for current state. Several items previously on this list — crates features passthrough, npm target-triple mapping, pypi dynamic-version handling, and PLAN_GAPS reconciliation — have since shipped; see the closed issues.
- None currently blocking.
Documented behaviours that look like gaps
Per-package tags, not a single shared version
A piot release tags each package independently as {name}-v{version} (e.g. my-crate-v0.3.1, my-py-v0.3.1). Consumers coming from a single-tag v0.3.1 layout will see two changes:
- Any install script or doc that parses
v*tags needs updating. - A single git
v{version}tag no longer maps to all three packages — you get three parallel tags instead.
This is intentional (each package can version independently when depends_on cascades don't fire) but it's a visible behavioural change at adoption.
bundled-cli is a packaging shape, not a builder
build = "bundled-cli" on a kind = "npm" package tells piot to publish a per-platform family ({name}-{target} sub-packages + top-level optionalDependencies). piot does not compile the binary that each sub-package carries — your workflow's build job does that. See npm platform packages for the full flow.
Combined CLI + napi under one top-level package
Each [[package]] picks one build mode. piot cannot publish a single my-lib top-level whose optionalDependencies mix both @myorg/cli-<slug> (CLI binaries via bundled-cli) and @myorg/lib-<slug> (napi addons via napi). If you need that shape, split into two published names (e.g. my-lib for the napi library, my-lib-cli for the CLI).
doctor's trust-policy check is opt-in and scoped
doctor validates OIDC trust-policy setup in layered phases, but only the first runs unconditionally:
- Trust policy (local) — always on. Verifies a publishing workflow exists,
id-token: write+contents: writeare granted, anenvironment:is pinned, and the publish step isn't commented out. - Trust policy (declared) — runs when any package declares
[package.trust_policy]inputitoutthere.toml. Diffs the declared workflow filename against the local workflow file and (in CI) againstGITHUB_WORKFLOW_REF. Without a declaration, the phase prints a neutral "not declared" line —doctordoes not infer intent. - Trust policy (crates.io registry) — opt-in, runs only when
CRATES_IO_DOCTOR_TOKENis set in the environment. Cross-checks the declaration against the trusted-publisher configs registered on crates.io.
For PyPI and npm there is no registry cross-check (neither exposes an API for it); the declared phase is the full gate there. See Authentication → Validating the trust-policy setup locally.