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
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
steps:
- name: Open or update the release PR, or create its approved release
id: release
uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0, node24
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
Expand Down
14 changes: 13 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ Post-stable invariants:
direct dependency's `result` to equal `success`. A skipped, cancelled,
failed, or missing dependency must never make build, live smoke,
publication, or registry verification eligible.
9. Keep Release Please pinned to the reviewed `v5.0.0` commit
`45996ed1f6d02564a971a2fa1b5860e934307cf7`, whose immutable action metadata
uses `node24`. The workflow semantic checker must reject any different pin.
10. `README.md` is the distribution long description and must remain accurate
before and after publication. Use `python -m pip install cometapi`,
unversioned project links, and publication-neutral maintenance language.
Reject approval, unpublished, exact-version installation, and versioned
release-link text in both source and built artifact metadata, and require
each built long description to exactly match the source README. Post-release
evidence changes must not rewrite README release state.

## Repository independence

Expand Down Expand Up @@ -329,7 +339,9 @@ committed.
evaluate skipped ancestry, reject cancellation and reruns, and require each
direct dependency to succeed.
- Keep README, roadmap, compatibility matrix, examples, and changelog aligned
with shipped behavior. Use currently supported model IDs.
with shipped behavior. README installation and availability guidance must be
publication-neutral because it is embedded in immutable distribution
metadata. Use currently supported model IDs.
- All repository documentation is written in English.

The Public Preview readiness record requires
Expand Down
12 changes: 12 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,18 @@ local mocked/package evidence
-> public registry digest, provenance, install, import, and mocked smoke
```

The package metadata embeds `README.md` as its long description. Because wheel,
sdist, and PyPI metadata are immutable, the README uses an unversioned install
command and publication-neutral release language that remains accurate before
and after a release. Source-document and artifact checks reject approval,
unpublished, exact-version installation, and versioned release-link text; each
artifact long description must also exactly match the source README.

Release Please is pinned to the immutable `v5.0.0` commit whose action metadata
uses `node24`. The workflow semantic contract fixes that SHA and runtime
disposition so GitHub does not need to force a deprecated Node 20 action onto a
newer runtime.

This complete trust chain executed successfully in
[release workflow run 30261746138](https://github.com/cometapi-dev/cometapi-python/actions/runs/30261746138)
for release commit `31b68904141489ca04932edbf305ccf88af09372`, recovery tag
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ automation.

## [Unreleased]

### Fixed

- Pin Release Please to its reviewed Node 24 action and reject regressions to
the deprecated Node 20 runtime.
- Keep the README and built distribution long description publication-neutral,
with an unpinned stable installation command and no transient approval state.

### Documentation

- Record completed `0.1.1` publication, live, provenance, digest, and
Expand Down
12 changes: 5 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CometAPI Python SDK

> **Stable release:** `0.1.1` is available from PyPI.
> **Stable release line:** `0.1.x` maintenance releases are available from PyPI.

`cometapi` is a thin Python adapter over the official OpenAI SDK for the
OpenAI-compatible CometAPI endpoint. It changes the default API key and base
Expand Down Expand Up @@ -29,16 +29,14 @@ Project links: [CometAPI](https://www.cometapi.com),

## Installation

Install the independently verified stable release from PyPI with:
Install the latest stable release from PyPI with:

```bash
python -m pip install 'cometapi==0.1.1'
python -m pip install cometapi
```

The immutable
[GitHub release](https://github.com/cometapi-dev/cometapi-python/releases/tag/v0.1.1)
and exact [PyPI release](https://pypi.org/project/cometapi/0.1.1/) record the
published artifact.
See the [CometAPI package on PyPI](https://pypi.org/project/cometapi/) for the
current stable distribution and release history.

## Authentication and configuration

Expand Down
37 changes: 26 additions & 11 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,26 @@ secret separation, and checkout-before-bundle-download ordering. Its git-backed
tests exercise accepted and rejected release histories locally; they still do
not emulate GitHub Actions.

The Release Please step is pinned to
`googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7`
(`v5.0.0`), whose immutable action metadata selects `node24`. The semantic
checker rejects any other pin so the workflow cannot silently regress to the
deprecated Node 20 runtime.

Release mode (`check_version.py --require-releasable-docs`) also fails closed
until project authorship, the canonical GitHub repository URL, the copyright
holder, security and support contacts, and the approved README/changelog
release state are present. Public Preview validation reports all discovered
violations in one run and still returns non-zero when any violation exists.
holder, security and support contacts, a publication-neutral README, and a dated
changelog release section are present. Public Preview validation reports all
discovered violations in one run and still returns non-zero when any violation
exists.

`pyproject.toml` embeds `README.md` as the immutable distribution long
description. The README therefore uses the unpinned
`python -m pip install cometapi` command and unversioned project links. Release
PRs and post-release evidence changes must not introduce approval, unpublished,
or exact-version availability statements. Artifact inspection applies the same
policy to wheel `METADATA` and sdist `PKG-INFO`, so source and registry-facing
descriptions cannot drift.

## Workflow responsibilities

Expand Down Expand Up @@ -326,7 +341,7 @@ feature or fix pull request
-> required offline CI
-> merge to the default branch
-> automated release pull request
-> human finalization of stable docs, metadata, and one-time bridge cleanup
-> human review of generated versions, changelog, and durable metadata
-> required release-PR CI, review, and merge
-> immutable tag and GitHub release
-> bounded API verification of immutable tag and commit identity
Expand All @@ -340,15 +355,15 @@ feature or fix pull request
-> roadmap milestone marked released
```

Stable `0.1.0` additionally requires the complete blocking Python matrix,
Stable `0.1.0` additionally required the complete blocking Python matrix,
executed README examples against the built package, trusted live evidence, and
reviewed release-PR and changelog agreement. Before the stable release PR is
merged, its finalization commit must state that `0.1.0` is approved for PyPI
publication, use the stable installation command and classifier, and remove the
one-time `last-release-sha` plus prerelease-versioning controls. The manifest,
reviewed release-PR and changelog agreement. Its one-time finalization removed
the `last-release-sha` and prerelease-versioning controls. Later maintenance
releases keep those controls absent and must retain publication-neutral README
metadata throughout the release and post-release sequence. The manifest,
project metadata, lock file, and changelog must remain at the exact generated
`0.1.0` version. If GitHub requires approval before checks run on the automated
pull request, approve only that reviewed workflow execution and wait for every
version. If GitHub requires approval before checks run on the automated pull
request, approve only that reviewed workflow execution and wait for every
blocking check.

## Immutable release publication recovery
Expand Down
7 changes: 4 additions & 3 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ Recorded pre-visibility dependency dispositions:
| Dependabot [PR #2](https://github.com/cometapi-dev/cometapi-python/pull/2): `actions/checkout` 4.2.2 to 7.0.1 | Closed unmerged; superseded by merged private [PR #9](https://github.com/cometapi-dev/cometapi-python/pull/9) | PR #2's [CI run 29796719306](https://github.com/cometapi-dev/cometapi-python/actions/runs/29796719306) failed because its regression test hard-coded the previous checkout SHA. PR #9 instead validates parsed action references independently of version, passed final CI run 29916685839, and squash-merged as `72b212dd72e66bbde9c6714329f72071cc1ca129`; PR #2 was closed without merging, and its failed run remains negative evidence only. |
| Dependabot [PR #3](https://github.com/cometapi-dev/cometapi-python/pull/3): `pypa/gh-action-pypi-publish` 1.14.0 to 1.14.1 | Deferred; keep out of `main` | Pull-request CI does not execute the release-triggered OIDC publish action or prove PyPI publication, provenance, or registry installation. Revisit with an authorized release-path review and the separately required protected release evidence; credential-free CI success alone is insufficient. |
| Dependabot [PR #4](https://github.com/cometapi-dev/cometapi-python/pull/4): `actions/upload-artifact` 4.6.2 to 7.0.1 | Closed unmerged; superseded by merged private [PR #9](https://github.com/cometapi-dev/cometapi-python/pull/9) | PR #9 applies the reviewed SHA pin in CI and release builds, requires missing artifacts to fail, retains digest evidence, passed final CI run 29916685839, and squash-merged as `72b212dd72e66bbde9c6714329f72071cc1ca129`; PR #4 was closed without merging. |
| Dependabot [PR #5](https://github.com/cometapi-dev/cometapi-python/pull/5): `googleapis/release-please-action` 4.4.1 to 5.0.0 | Deferred; keep out of `main` | `RELEASE_PLEASE_ENABLED` remains disabled, and pull-request CI does not execute the gated write-capable Release Please action. Revisit only after its real config, manifest, permissions, and release behavior can be reviewed without treating a skipped action as execution evidence. |
| Dependabot [PR #5](https://github.com/cometapi-dev/cometapi-python/pull/5): `googleapis/release-please-action` 4.4.1 to 5.0.0 | Superseded by authorized replacement [PR #29](https://github.com/cometapi-dev/cometapi-python/pull/29); keep PR #5 out of `main` and close it after PR #29 merges | PR #29 pins the reviewed upstream `v5.0.0` commit, verifies its Node 24 runtime through the workflow semantic contract, and carries the release-metadata hardening required for `0.1.2`. PR #5's failed CI is negative evidence from the former semantic check that required the old action pin; it must not be merged independently or treated as runtime evidence. |
| Dependabot [PR #6](https://github.com/cometapi-dev/cometapi-python/pull/6): `actions/setup-python` 5.6.0 to 7.0.0 | Closed unmerged; superseded by merged private [PR #9](https://github.com/cometapi-dev/cometapi-python/pull/9) | PR #9 applies the reviewed SHA pin across CI, monitoring, and release workflows, passed final CI run 29916685839 on every blocking lane, and squash-merged as `72b212dd72e66bbde9c6714329f72071cc1ca129`; PR #6 was closed without merging. |

Recorded pre-visibility replacement evidence on 2026-07-22:
Expand Down Expand Up @@ -346,8 +346,9 @@ Public Preview remains ready only while:
self-review.
- Default-branch CI is rerun successfully after the public configuration.
- The fail-closed content gate reports all violations together and then passes,
repository self-containment and package gates pass, and the README accurately
states prerelease and registry availability.
repository self-containment and package gates pass, and the README uses
publication-neutral stable installation guidance suitable for immutable
distribution metadata.
- An explicitly authorized protected live smoke passes within the four-request,
16-output-token, 30-second-per-request, concurrency-one, stop-on-first-failure
budget.
Expand Down
57 changes: 45 additions & 12 deletions scripts/_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import sys
from collections.abc import Iterable
from email.message import Message
from email.parser import Parser
from pathlib import Path
from typing import cast

Expand All @@ -31,6 +32,26 @@
"Support": f"{CANONICAL_REPOSITORY}/blob/main/SUPPORT.md",
"Security": CANONICAL_SECURITY,
}
PUBLIC_README_INSTALL_COMMAND = "python -m pip install cometapi"
PUBLIC_README_FORBIDDEN_PATTERNS = (
(r"(?i)\bpending[\s-]+owner(?:ship|s)?\b", "pending owner identity"),
(r"(?i)\bapproved\s+for\s+pypi\s+publication\b", "publication approval state"),
(r"(?i)\b(?:candidate|unreleased|unpublished)\b", "unpublished release state"),
(r"(?i)\bno\s+pypi\s+publication\b", "missing PyPI publication"),
(r"(?i)\b(?:has\s+)?not\s+been\s+published\b", "unpublished release state"),
(
r"(?i)\bdo\s+not\s+treat\b[^\n]*\bcurrently\s+available\b",
"temporary availability warning",
),
(r"(?i)\blocal\s+candidate\b", "local-candidate narrative"),
(r"(?i)\b0\.1\.\d+(?:a\d+)?\s+is\s+(?:available|approved)\b", "versioned release state"),
(r"(?i)cometapi==\d+\.\d+\.\d+(?:a\d+)?", "version-pinned installation command"),
(r"https://pypi\.org/project/cometapi/\d", "versioned PyPI release link"),
(
r"https://github\.com/cometapi-dev/cometapi-python/releases/tag/v\d",
"versioned GitHub release link",
),
)


class CheckError(RuntimeError):
Expand Down Expand Up @@ -96,25 +117,37 @@ def sha256_file(path: Path) -> str:

def parse_metadata(raw: bytes, source: str) -> Message:
"""Parse package core metadata and require its identity fields."""
message = Message()
text = raw.decode("utf-8")
for line in text.splitlines():
if not line.strip():
break
if line[0].isspace():
current = message.get_payload()
message.set_payload(f"{current}\n{line}" if current else line)
continue
name, separator, value = line.partition(":")
if separator:
message[name] = value.strip()
try:
message = Parser().parsestr(raw.decode("utf-8"))
except (UnicodeDecodeError, ValueError) as exc:
raise CheckError(f"{source}: invalid metadata: {exc}") from exc
if message.get("Name") != DIST_NAME:
raise CheckError(f"{source}: expected Name: {DIST_NAME!r}")
if message.get("Version") is None:
raise CheckError(f"{source}: missing Version metadata")
return message


def metadata_description(message: Message, source: str) -> str:
"""Return the rendered long description from a distribution metadata message."""
payload = message.get_payload()
if not isinstance(payload, str) or not payload.strip():
raise CheckError(f"{source}: missing long description metadata")
return payload


def public_readme_release_violations(text: str) -> list[str]:
"""Return transient or version-specific release statements in public README text."""
return [
label for pattern, label in PUBLIC_README_FORBIDDEN_PATTERNS if re.search(pattern, text)
]


def public_readme_has_install_command(text: str) -> bool:
"""Return whether the public README contains the exact unpinned install command."""
return any(line.strip() == PUBLIC_README_INSTALL_COMMAND for line in text.splitlines())


def require_equal_versions(items: Iterable[tuple[str, str]]) -> str:
"""Require all named version values to normalize to the same version."""
normalized = [(name, normalize_version(value)) for name, value in items]
Expand Down
Loading