Package publishing policy
AGILAB publishes a coordinated set of Python packages to PyPI so public installs, notebook examples, worker environments, and release evidence can resolve the same versioned runtime without relying on a source checkout.
Important
Publication policy and already published versions must be distinguished
from the native source migration. The source ui and notebook
profiles now use the React host and Python facade. Existing PyPI releases
and hosted Spaces keep their recorded behaviour until publication; the
local migration does not update those releases. See
Native React UI and notebook export for source instructions.
User-facing install surfaces
For public users, the supported entry points are:
agilab: the top-level CLI package. Add theuiextra for the local interface supplied by that release, or theexamplesextra for public built-in apps and notebooks.agi-core: the compact notebook/API runtime used by the public notebook examples.
Documentation, release notes, quick starts, and demos should point users to those surfaces first.
Internal runtime packages
The following core runtime packages are also published to PyPI:
agi-envagi-nodeagi-cluster
They are runtime building blocks used by agi-core, agilab, app worker
environments, and CI validation. Their publication is intentional, but they are
not promoted as independent user products.
Published UI support packages
agi-gui is also published to PyPI from src/agilab/lib/agi-gui. It is
not part of the core runtime: it depends on the headless agi-env package and
adds UI dependencies used by AGILAB pages and page bundles. The native Python
facade described here is the source implementation; older published versions
retain their recorded provider and dependencies.
Worker environments should keep using agi-env unless they explicitly need
to render UI.
agi-web is published to PyPI from src/agilab/lib/agi-web. It defines
portable, evidence-backed web component payloads for app-owned rich UI islands.
The current source package supplies the native React host, Python view
facade, notebook widgets and static component exports. Its compiled rendering
assets are bundled locally; application computation and scientific dependencies
remain in the app package.
Published page-bundle packages
The public analysis pages are built and published as self-contained page-bundle package artifacts. Each package carries one generic analysis page bundle and is distributed on PyPI as both a wheel and source distribution:
agi-page-simplex-mapagi-page-decision-evidenceagi-page-timeseries-forecastagi-page-inference-reportagi-page-live-artifactsagi-page-geospatial-mapagi-page-geospatial-3dagi-page-network-mapagi-page-routing-model-comparisonagi-page-queue-healthagi-page-relay-healthagi-page-scenario-cockpitagi-page-promotion-gateagi-page-feature-attributionagi-page-app-uiagi-page-training-report
These packages are built as both wheels and source distributions. Their names stay app-agnostic because page bundles must be reusable by AGILAB apps and exported notebooks.
Published page-bundle umbrella package
agi-pages is published to PyPI from src/agilab/lib/agi-pages as both a
wheel and source distribution. It is an umbrella/provider package for page
bundle discovery and exposes agi_pages.bundles_root() plus
bundle-resolution helpers so agi-env and exported notebooks can discover
installed page bundles without making base agilab depend on them.
agilab[ui] pulls agi-pages for the local ANALYSIS page;
agilab[pages] installs the page provider without the full UI profile.
App project packages
The public built-in app projects are built as self-contained app project
package artifacts. Each package carries one project payload and exposes it
through the agilab.apps entry point group so agi-env can resolve
installed apps without the monorepo checkout once that package is installed:
For a user-facing map from PROJECT names to package names, PyPI status, and recommended use cases, see Public app catalog.
agi-app-mission-decisionagi-app-pandas-executionagi-app-polars-executionagi-app-flight-telemetryagi-app-multi-dagagi-app-weather-forecastagi-app-sklearn-pipelineagi-app-data-quality-gateagi-app-pytorch-playgroundagi-app-learning-assessmentagi-app-tescia-diagnostic(compatibility dependency onagi-app-learning-assessment; no duplicate app payload)agi-app-uav-queueagi-app-uav-relay-queueagi-app-minimalagi-app-r-runtime-bridge
Eleven app payload packages are promoted to PyPI in the current release plan:
agi-app-mission-decision, agi-app-pandas-execution,
agi-app-polars-execution, agi-app-flight-telemetry,
agi-app-multi-dag, agi-app-weather-forecast,
agi-app-sklearn-pipeline, agi-app-data-quality-gate,
agi-app-pytorch-playground, agi-app-learning-assessment, and
agi-app-uav-relay-queue. The remaining app project payload,
agi-app-uav-queue, together with agi-app-minimal and
agi-app-r-runtime-bridge, is also built as a wheel and source-distribution
artifact and kept in the GitHub Release distribution archive until it is
explicitly promoted. Each payload is staged during package build, with local
virtual environments, compiled artifacts, locks, and generated build outputs
excluded.
Together these fourteen payloads cover every built-in source project. The
examples profile installs the eleven promoted apps; install the other
three app wheels from the matching GitHub Release distribution archive when
needed. The R bridge additionally requires an installed Rscript runtime;
its Python package does not bundle R. Scientific dependencies and datasets
remain app-specific and must be installed before executing a selected app.
End users can add a trusted promoted app package to an existing AGILAB
environment from PROJECT through agi-app or from the CLI with
agilab app install. Both routes accept one agi-app-* package
requirement, use uv pip install --python against the current Python
environment, and rely on the package’s agilab.apps entry point for
discovery. agilab app list, agilab app update, and agilab app remove
manage installed agi-app payloads without copying them into
src/agilab/apps. PROJECT and agilab app check expose package
preflight metadata, including Python compatibility, wheel/sdist availability,
app entry points from wheel metadata, file hashes, and advertised
provenance/signature status. APPS_REPOSITORY remains the source-checkout
mechanism for external app repositories.
minimal_app_project is not published as a separate agi-app-* distribution.
It is the single base starter template bundled inside agi-apps so packaged
users can inspect or copy a minimal app scaffold without treating it as a
standalone demo package.
Published app/example umbrella package
agi-apps is published to PyPI from src/agilab/lib/agi-apps as both a
wheel and source distribution. It is an umbrella/catalog package: it keeps the
lightweight agilab.apps.install helper and agilab.examples learning
assets, and it depends on the app payload packages already promoted to PyPI.
The root agilab wheel stays lean; agilab[core] pulls the compact runtime,
while agilab[ui] and agilab[examples] pull agi-apps when the
packaged first-proof or demo assets are needed.
Why keep them published
Publishing these runtime packages keeps the release process reproducible:
pip install agilabanduvx agilabcan resolve the exact lightweight command-shell graph without forcing runtime, UI, app, page, notebook, or model dependencies.pip install "agilab[core]"installs the matchingagi-corepackage foragilab dry-runand compact notebook/API runtime checks.pip install "agilab[ui]"installs the matchingagi-guipackage,agi-webcomponent contract, and native React UI page dependencies for the local web interface, plusagi-apps, its per-app project dependencies, andagi-pagesso the UI opens with the baseminimal_app_projecttemplate, promoted app packages, and analysis views available.pip install "agilab[examples]"installsagi-appsand its per-app project dependencies plus the baseminimal_app_projectstarter template and notebook/demo helper dependencies for public packaged examples.pip install "agilab[pages]"installsagi-pagesfor analysis page bundle discovery without also pulling public built-in apps.agi-corecan pin the matchingagi-env,agi-node, andagi-clusterversions for a release.agilab[ui]can pin the matchingagi-guiversion for the UI/page surface without making CLI/core installs depend on native React UI.App worker environments can install the same runtime components in isolation from the manager environment.
CI and release evidence can validate the same dependency graph that external users receive from PyPI.
Release rule
For each public release, publish only the packages whose own payload or curated dependency graph changed. AGILAB uses independent version tracks:
runtime components such as
agi-env,agi-node,agi-cluster,agi-gui, andagi-webversion the implementation they carry;bundle packages such as
agi-core,agi-pages,agi-apps, and the rootagilabversion the curated dependency graph they expose;payload packages such as
agi-page-*andagi-app-*version the page or app payload they carry;agi-page-*payloads and explicitly promotedagi-app-*payloads are published to PyPI, while unpromoted app payloads remain release artifacts until publication is enabled.
Bundle packages should exact-pin the component versions they curate for
reproducible installs. Payload packages should declare compatible AGILAB runtime
ranges instead of exact-pinning every AGILAB release, so a runtime patch does
not force republishing unchanged pages or apps. Do not skip agi-node,
agi-cluster, agi-gui, agi-web, agi-pages, agi-apps,
agi-page-*, or the root agilab package from the PyPI publish matrix
when their own version or dependency graph changed. Keep unpromoted app-project
payload packages in the release artifact matrix even when their PyPI upload
flag is disabled.
If AGILAB later embeds the agi_node and agi_cluster Python modules
directly into a single wheel, that migration must update dependency metadata,
installer tests, notebook examples, and the release preflight before the
standalone runtime packages can be retired from the public publish flow.
Release synchronization contract
Release automation is intentionally conservative because AGILAB publishes components, bundles, and payload packages. A public release may have several package versions, but it must have one committed dependency graph before files are uploaded to real PyPI.
The release commit should synchronize:
the root
agilabversion when the top-level bundle changes,each changed component, bundle, page payload, or app payload package version,
exact internal dependency pins used by bundle packages,
compatible runtime lower bounds used by page/app payload packages,
built-in app payload metadata and runtime requirements,
public README badges, release proof references, and docs mirror stamp when they changed.
Real PyPI publication may skip distributions that already exist for the same
package version, but it must not rewrite versions or dependency metadata during
the upload job. If a package needs a different version, choose it explicitly,
regenerate the release commit, rerun preflight, and publish from that committed
state. TestPyPI rehearsals may still use retry-oriented .postN versions,
and release-candidate versions such as YYYY.MM.DDrc1 are acceptable for
preview validation, but those rehearsals are not the source of truth for a real
final release.
By default, the GitHub PyPI workflow asks tools/release_plan.py to omit
selected PyPI packages whose expected wheel/sdist artifacts for the committed
project version already exist on PyPI. Those skipped packages are not sent to
the build/upload matrix, PyPI provenance verification, cleanup planning, or
GitHub release-asset assembly. This keeps split packages independent in
practice: unchanged packages do not need a new version and do not trigger slow
PyPI web cleanup. Use the manual include_existing_pypi=true dispatch input
only when intentionally repairing release assets from already-published PyPI
artifacts.
For behavior releases where only part of the tree changed, maintainers should
compute the release set from impact rather than bumping every package. The
planner supports --impact-base-ref <previous-release-tag>: it maps changed
paths to package payloads, then adds only transitive bundle packages that
exact-pin those changed packages. For example, a PyTorch playground change
selects agi-app-pytorch-playground, agi-apps, and agilab; an
agi-env runtime change selects agi-env plus the runtime/UI/root bundles
that exact-pin it, but not unrelated app payload packages. The
pypi-publish workflow exposes the same behavior with the
impact_base_ref dispatch input, ignored when explicit packages or
roles are supplied.
The local release helper has the same selector for preparing a release commit:
uv --preview-features extra-build-dependencies run python tools/pypi_publish.py \
--repo pypi \
--version YYYY.MM.DD[.postN] \
--impact-base-ref vYYYY.MM.DD \
--dry-run \
--skip-cleanup \
--no-pypirc-check
Use the printed package list as the release bump scope. Do not bump unchanged
package pyproject.toml files just to keep a global version number aligned.
PyPI cleanup is separate from publication. The release workflow performs builds, Trusted Publishing, provenance checks, GitHub assets and Hugging Face sync without deleting historical releases or waiting for web-login cleanup.
The manual pypi-release-retention workflow produces a reviewable plan only.
Its required delete_releases input lists exact package==version targets,
one per line. It has read-only repository permissions, no PyPI credentials and
no deletion step. The local tools/pypi_release_retention.py command uses the
same exact selection, defaults to planning, and rejects implicit package-wide
cleanup, duplicate targets, unknown packages or versions, current project
versions, exact first-party dependency pins and each project’s latest release.
Before a deletion, review consumers and save every wheel and source archive.
Use --backup-dir during planning to create an archive and hash manifest.
Confirmed local deletion requires that existing backup, --confirm-delete,
fresh official metadata and matching hashes for the complete file set. The
command rechecks protections, the complete current file set and saved bytes
after web authentication and immediately before each selected deletion, stops
on authentication failure and verifies that selected releases are absent while
unselected releases remain present. It never reports an authentication warning
as successful cleanup.
PyPI web-management authentication is independent of OIDC uploads. An unknown device can require email confirmation. Prefer a confirmed browser session on the maintainer’s machine; a passkey or security key can avoid the extra unknown-device check used with TOTP. A browser session does not authenticate the command’s separate HTTP session. Complete any authentication in PyPI, without weakening repository approval gates or exposing credentials. PyPI deletion is irreversible: archived files remain available locally, but the deleted release cannot be restored and its files cannot be uploaded again. The local command examples and browser procedure are maintained in docs/pypi-cleanup-maintainer-guide.md.
The required preflight is the place to catch synchronization drift. It should
validate package metadata, internal pins, dependency-policy hygiene, docs mirror
integrity, installer behavior, and release-proof consistency before either the
library packages or the umbrella agilab wheel are uploaded.
After PyPI provenance and GitHub release assets succeed, the public release
workflow runs tools/hf_space_release_sync.py against the release source
snapshot. That job requires the HF_TOKEN repository secret, deploys the
bounded first-proof Hugging Face Space, waits for the hosted runtime to report
the deployed Space commit, runs tools/hf_space_smoke.py, and commits the
Space commit back into the public release-proof docs. A missing or invalid
HF_TOKEN is a release failure, not a warning.
Publishing authentication
Real PyPI publication must use GitHub OIDC Trusted Publishing. Long-lived PyPI
API tokens are not part of the normal release path. If a package or repository
is not configured as a PyPI trusted publisher, the publish workflow should stop
with an explicit configuration error instead of falling back to a stored token.
PyPI may still describe an upload as using an API token because the Trusted
Publishing flow exchanges the GitHub OIDC identity for a short-lived PyPI upload
token. That upload token is minted by PyPI for the matched project and workflow;
it is not a stored repository secret, not PYPI_API_TOKEN, not
PYPI_TOKEN, and not TWINE_PASSWORD.
Each PyPI project selected for upload must have a GitHub trusted publisher entry
matching the release workflow claims exactly. The workflow renders the same contract with
tools/pypi_trusted_publisher_contract.py before publication and appends the
per-package claim to the GitHub step summary before each upload. A PyPI
invalid-publisher error means the GitHub OIDC token was valid, but the PyPI
project did not have a matching publisher entry or one of the fields differed.
After publication, tools/pypi_provenance_check.py verifies the PyPI
integrity endpoint for every selected published package and fails the workflow
before GitHub release assets are published if any wheel or sdist lacks a Trusted
Publishing attestation.
Configure these entries in each PyPI project under
Settings > Publishing > Trusted publishers > Add GitHub publisher. Entries
for payload packages not yet marked publish_to_pypi=true are activation
targets for future PyPI uploads; the current PyPI release path only requests
OIDC tokens for packages marked
publish_to_pypi=true in tools/release_plan.py:
PyPI project |
Owner |
Repository |
Workflow |
Environment |
|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The corresponding OIDC subject for each row is
repo:ThalesGroup/agilab:environment:<environment>. Rows whose generated
release-plan entry has publish_to_pypi = "false" are pre-registered for a
future promotion but are skipped by the real PyPI publish step; their artifacts
remain GitHub Release archive payloads until promotion.
The local tools/pypi_publish.py helper may still build packages and publish
to TestPyPI for rehearsals, but local twine upload to real PyPI is disabled by
default. Real PyPI files should come from .github/workflows/pypi-publish.yaml
so PyPI metadata shows Trusted Publishing/OIDC provenance. Any break-glass local
twine upload requires AGILAB_ALLOW_LOCAL_PYPI_TWINE=1 and a documented
release exception.
Release managers can run the same provenance gate locally after a publish run:
python tools/pypi_provenance_check.py --json
Release evidence should continue to record the bounded nature of the public proof: package smoke tests, docs, and hosted demos are useful evidence, but they do not certify private clusters, sensitive-data deployments, or production operations.
GitHub deployment environments
The real PyPI workflow uses one GitHub deployment environment per PyPI project
so Trusted Publishing receives a project-specific OIDC claim. The list also
keeps pre-registered archive-only app payload environments from the generated
release plan, but those entries are skipped while publish_to_pypi is
false:
pypi-agi-envpypi-agi-nodepypi-agi-clusterpypi-agi-corepypi-agi-guipypi-agi-webpypi-agi-page-simplex-mappypi-agi-page-decision-evidencepypi-agi-page-timeseries-forecastpypi-agi-page-inference-reportpypi-agi-page-live-artifactspypi-agi-page-geospatial-mappypi-agi-page-geospatial-3dpypi-agi-page-network-mappypi-agi-page-routing-model-comparisonpypi-agi-page-queue-healthpypi-agi-page-relay-healthpypi-agi-page-scenario-cockpitpypi-agi-page-promotion-gatepypi-agi-page-feature-attributionpypi-agi-page-app-uipypi-agi-page-training-reportpypi-agi-pagespypi-agi-app-mission-decisionpypi-agi-app-pandas-executionpypi-agi-app-polars-executionpypi-agi-app-flight-telemetrypypi-agi-app-multi-dagpypi-agi-app-weather-forecastpypi-agi-app-sklearn-pipelinepypi-agi-app-data-quality-gatepypi-agi-app-pytorch-playgroundpypi-agi-app-learning-assessmentpypi-agi-app-tescia-diagnosticpypi-agi-app-uav-queuepypi-agi-app-uav-relay-queuepypi-agi-appspypi-agilab
The old bare pypi environment belongs to the retired single-package
publisher and must not be recreated or referenced by workflows. If GitHub’s
repository landing page shows pypi inactive in the Deployments widget, it is
stale deployment history, not a current AGILAB component. Maintainers should
delete only the retired pypi deployment records/environment and keep the
package-specific environments above.
Release cadence and post releases
AGILAB uses date-based public versions. A normal public release should advance
to a deliberate new version. Real PyPI publication must not silently auto-create
.postN releases when a version collision is detected; the release tool is
expected to stop and require an explicit version choice instead.
Public PyPI .postN releases are forbidden for new AGILAB
publications. The pypi-publish workflow enforces this with
tools/pypi_release_version_policy.py and an explicit release_mode:
stable accepts YYYY.MM.DD, hotfix accepts the PyPI package version
YYYY.MM.DD.N and the GitHub release tag vYYYY.MM.DD_N for a same-day
public fix, candidate accepts YYYY.MM.DDrcN, and repair is reserved
for release-evidence repair flows that must not publish PyPI distributions.
The dense April-May 2026 .postN history records public-beta hardening of the
split package release pipeline and is kept visible for auditability. It is not
the target steady-state release rhythm: normal feature or behavior changes
should advance to a deliberate new date-based release, and same-day public
hotfixes should use release_mode=hotfix with package version
YYYY.MM.DD.N and release tag vYYYY.MM.DD_N instead of a post-release
suffix. TestPyPI rehearsals are the exception: retry-oriented .postN bumps
are allowed there because TestPyPI is often reused during dry runs.
Release-candidate versions such as
YYYY.MM.DDrc1 are also acceptable for rehearsal when a public pre-release is
useful before the final date-based version.
Typing policy
The root package and core runtime packages set disallow_untyped_defs = true
for project code. The repository also keeps a curated strict slice runnable via
tools/shared_core_strict_typing.py or the shared-core-typing workflow
profile; that slice includes agi-core and selected shared support modules
and runs mypy with --strict.
ignore_missing_imports = true is still used at package boundaries to avoid
making third-party stub availability a blocker for runtime development. That
setting should not be read as permission for untyped AGILAB APIs: new public
runtime code should remain typed, and the curated strict slice should expand as
shared APIs stabilize.
Packaging notes
setup.py is intentionally kept alongside pyproject.toml. It is not a
leftover from an incomplete packaging migration. pyproject.toml remains the
canonical source for PyPI metadata, dependency resolution, and uv-based
workflows, while setup.py is the compatibility build entry point used by the
Dask worker distribution path that still emits .egg artifacts.
Removing setup.py is valid only after the worker distribution path no
longer depends on egg packaging, and that migration must update the build
helpers, installer tests, distributed execution tests, and release preflight in
the same change.