Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions README.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,10 +426,13 @@ d'une release — tag git, `VERSION`, métadonnées du paquet, wheel/sdist, nom
`VERSION` interne du bundle — est fail-closed : un tag qui ne nomme pas l'arbre qu'il pointe
ne peut pas publier.

Les releases parlent un protocole de mise à jour versionné (v2) : le bundle est
`ainative-lifecycle-v2-<version>.zip` et un runtime antérieur à v2.2.2 ne peut pas le consommer
même depuis un miroir — le format lui-même le refuse. Chaque artefact publié porte aussi une
attestation de provenance de build GitHub (`gh attestation verify <file> -R Rwanbt/ai-native-dev-stack`) ;
Les releases parlent un protocole de mise à jour versionné (v3) : un manifeste externe
(`ainative-release-v3.json`) est vérifié contre les métadonnées de la source de release avant
tout le reste, puis il ancre le bundle de cycle de vie
(`ainative-lifecycle-v3-<version>.zip`) par nom, version, taille et SHA-256. Une release
pont peut encore parler v2 (`ainative-lifecycle-v2-<version>.zip`), et un runtime antérieur à
v2.2.2 ne peut pas en consommer un, même depuis un miroir — le format lui-même le refuse.
Chaque artefact publié porte aussi une attestation de provenance de build GitHub (`gh attestation verify <file> -R Rwanbt/ai-native-dev-stack`) ;
SHA-256 seul signifie intégrité, pas signature humaine (voir docs/RELEASING.md).

---
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,10 +420,14 @@ chain - git tag, `VERSION`, package metadata, wheel/sdist, bundle filename and
bundle internal `VERSION` - is fail-closed: a tag that does not name the tree it
points at cannot publish.

Releases speak a versioned update protocol (v2): the lifecycle bundle is
`ainative-lifecycle-v2-<version>.zip` and a runtime older than v2.2.2 cannot
consume one even when a mirror hands it the file - the format itself refuses it.
Every published artifact also carries a GitHub build-provenance attestation
Releases speak a versioned update protocol (v3): an external manifest
(`ainative-release-v3.json`) is verified against the release source's own
metadata before anything else, and it anchors the lifecycle bundle
(`ainative-lifecycle-v3-<version>.zip`) by name, version, size and SHA-256. A
bridge release can still speak v2 (`ainative-lifecycle-v2-<version>.zip`), and
a runtime older than v2.2.2 cannot consume one even when a mirror hands it the
file - the format itself refuses it. Every published artifact also carries a
GitHub build-provenance attestation
(`gh attestation verify <file> -R Rwanbt/ai-native-dev-stack`); SHA-256 alone
still means integrity, not a human signature (see docs/RELEASING.md).

Expand Down
2 changes: 1 addition & 1 deletion SUPPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
| Harnesses | Claude Code, Codex, OpenCode, Cursor, Gemini CLI, MiniMax/Mavis — for the shared method, skills and hooks |
| Work management | Generic Git; GitHub (mapping); GitLab (mapping). The Work Authority is resolved locally, purely and fail-closed (ADR-0018) |
| Features | Project-scope features independent of the profile (`forge-github` default, `forge-gitlab`), State schema V2 (ADR-0017) |
| Release sources | GitHub.com (built-in, qualified); a local mirror; an anonymous HTTPS release API (`AINATIVE_UPDATE_URL`, never credentialed); GitLab.com (qualified live: Generic Package Registry anchor, managed releases, exact version chain — 2026-09-16); GitLab Self-Managed and GitHub Enterprise Server UNTESTED — **not supported** |
| Release sources | GitHub.com (built-in, qualified live — full V3 chain against the published v2.5.0, 2026-09-17); a local mirror; an anonymous HTTPS release API (`AINATIVE_UPDATE_URL`, never credentialed); GitLab.com (qualified live: Generic Package Registry anchor, managed releases, exact version chain — 2026-09-16); GitLab Self-Managed and GitHub Enterprise Server UNTESTED — **not supported** |

Only the latest release is supported; fixes ship as a new patch version.

Expand Down
35 changes: 22 additions & 13 deletions UPDATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,18 @@ The refusal names both versions and prints the exact upgrade command.
`ainative status --check-updates` and `ainative doctor --check-updates` still
report what is available and say when the runtime must be upgraded first.

Releases from the protocol v2 line cannot be consumed by a runtime older than
v2.2.2 at all: the bundle is named `ainative-lifecycle-v2-<version>.zip` and its
payload sits under `stack/`, which the old selector and the old extractor both
refuse. That containment is deliberate - an old runtime must never apply a
policy it does not know - and the only path forward is upgrading the CLI, then
running `ainative update` in each project.
Releases now speak protocol v3: an external manifest
(`ainative-release-v3.json`) anchors the lifecycle bundle
(`ainative-lifecycle-v3-<version>.zip`), and the bridge release v2.4.4 is the
stop where runtimes that only know protocol v2 are told, in their own
vocabulary, that a CLI upgrade is required first (`CLI_UPDATE_REQUIRED`).
Runtimes older than the bridge refuse a v3 release fail-closed and the only
path forward is the documented manual CLI upgrade, then `ainative update` in
each project. Protocol v2 bridge releases (for example
`ainative-lifecycle-v2-2.4.4.zip`) remain consumable by runtimes from v2.2.2;
a runtime older than v2.2.2 cannot consume one even when a mirror hands it the
file — the format itself refuses it (the payload sits under `stack/`, which
the old selector and the old extractor both refuse).

### What it guarantees

Expand Down Expand Up @@ -257,14 +263,17 @@ does what it always did, now through the lifecycle manager.
2. Bump `VERSION` (semver) and the `stack-version` header in `AGENTS.md`.
3. Move `CHANGELOG.md` `[Unreleased]` items under `## [x.y.z] - YYYY-MM-DD`.
4. Tag: `git tag vX.Y.Z && git push --tags`.
5. Publish the release assets: wheel, sdist and the lifecycle bundle
`ainative-lifecycle-v2-x.y.z.zip` (built by
5. Publish the release assets: wheel, sdist, the lifecycle bundle
`ainative-lifecycle-v3-x.y.z.zip` and its external manifest
`ainative-release-v3.json` (both built by
`scripts/build_lifecycle_bundle.py`; `.github/workflows/release-assets.yml`
builds, gates and attests all three, then publishes the release from a
verified draft). `ainative update check` resolves the latest release from
that source; the bundle's published SHA-256 is verified before any file is
written, and a release without a published digest for the bundle is refused
(`UPDATE_INTEGRITY_METADATA_MISSING`). See `docs/RELEASING.md` for the full
builds, gates and attests all of them, then publishes the release from a
verified draft — a bridge release can still be built with
`--protocol 2`). `ainative update check` resolves the latest release from
that source; the manifest's anchor (SHA-256 + size from the provider's
metadata) and the bundle's published digest are verified before any file is
written, and a release without them is refused (`CHECK_FAILED` /
`UPDATE_INTEGRITY_METADATA_MISSING`). See `docs/RELEASING.md` for the full
sequence, the attestations and the PyPI half.

Users then see `UPDATE_AVAILABLE` and update with one command — no personal
Expand Down
38 changes: 23 additions & 15 deletions docs/DISTRIBUTION-LIFECYCLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,12 +429,18 @@ that can refuse happens before the first project write: a refused update -
wrong runtime, wrong version, bad digest, bad archive - leaves the project
byte-identical, cache included.

**The artifact, stated precisely.** The official provider consumes exactly one
asset: the lifecycle bundle `ainative-lifecycle-v2-<version>.zip` published
beside the wheel and the sdist, named for the release's own version. Inside,
a `lifecycle-protocol.json` document names the protocol version, the release
**The artifact, stated precisely.** The official provider consumes the release's
declared pair: the external manifest `ainative-release-v3.json` published beside
the wheel, the sdist and the lifecycle bundle `ainative-lifecycle-v3-<version>.zip`,
each named for the release's own version. The manifest is parsed only after its
anchor — the SHA-256 and size the provider's own metadata reports for it — is
verified; it then declares the bundle's name, version, SHA-256 and size, and the
bundle is verified against that declaration. Inside the bundle, a
`lifecycle-protocol.json` document names the protocol version, the release
version and the payload root (`stack/`), and the payload's `VERSION` is compared
with the release again.
with the release again. A bridge release can still be built with `--protocol 2`
(`ainative-lifecycle-v2-<version>.zip`, no manifest) so older runtimes have the
documented upgrade stop.

**Containment of pre-2.2.2 runtimes, stated precisely.** Runtimes up to v2.2.2
looked for `ainative-dev-stack-*.zip` and found their payload by taking the
Expand Down Expand Up @@ -470,16 +476,18 @@ cd your-project && ainative update

**A newer lifecycle protocol, stated precisely.** A release that publishes a
lifecycle bundle or manifest for a protocol newer than the running runtime's
(`ainative-lifecycle-v3-*`, `ainative-release-v3.json`) is not a broken
release: `update check` reports it as available with a CLI upgrade required,
and `ainative update` refuses it with `CLI_UPDATE_REQUIRED`, before any
download or write, naming the upgrade command. A release that publishes no
(the current line is protocol 3: `ainative-lifecycle-v3-*`,
`ainative-release-v3.json`) is not a broken release *to a runtime that knows
the bridge*: `update check` reports it as available with a CLI upgrade
required, and `ainative update` refuses it with `CLI_UPDATE_REQUIRED`, before
any download or write, naming the upgrade command. A runtime older than the
bridge release has no such vocabulary and refuses it fail-closed (`CHECK_FAILED:
release publishes no ainative-lifecycle-v2-*.zip lifecycle bundle` on
`update check`); its documented escape path is the same upgrade command,
applied manually, then `ainative update`. A release that publishes no
lifecycle bundle at all keeps its refusal:
`UPDATE_INTEGRITY_METADATA_MISSING`. A runtime older than the bridge release
sees a V3 release as `UPDATE_INTEGRITY_METADATA_MISSING`; its documented
escape path is the same upgrade command, applied manually, then
`ainative update`. Before the first V3 publication the release workflow
verifies the declared `V3_BRIDGE_RELEASE` with
`UPDATE_INTEGRITY_METADATA_MISSING`. Before the first V3 publication the
release workflow verifies the declared `V3_BRIDGE_RELEASE` with
`scripts/check_bridge_release.py`, which resolves it through these same
selection rules and blocks the publication (`BLOCK RELEASE`) when the bridge
cannot be verified.
Expand Down Expand Up @@ -629,7 +637,7 @@ Files the stack never wrote are in the "kept" column of every row.
| Number | Source of truth | Changes when |
|---|---|---|
| Stack release | `VERSION` | a release is cut |
| Update protocol | `provider.UPDATE_PROTOCOL_VERSION` | the bundle format changes (currently 2) |
| Update protocol | `release_v3.BUNDLE_PROTOCOL_VERSION` (the current line, 3; the legacy v2 path keeps `provider.UPDATE_PROTOCOL_VERSION = 2`) | the bundle format changes |
| Lifecycle state schema | `state.SCHEMA_VERSION` | `state.json`'s shape changes |
| Work Plane runtime | `ainative_workplane.__version__` | the verdict engine is released |
| Artifact schema | `contracts.SUPPORTED_SCHEMA_VERSIONS` | a Work Plane artifact changes shape |
Expand Down
Loading