Skip to content
Draft
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
75 changes: 75 additions & 0 deletions .github/workflows/production-lifecycle-policy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Production lifecycle policy

on:
schedule:
- cron: "37 8 * * *"
workflow_dispatch:
pull_request:
paths:
- ".github/workflows/production-lifecycle-policy.yml"
- "docs/production-lifecycle.json"
- "docs/reference/production-lifecycle.md"
- "docs/schemas/production-lifecycle-public.schema.json"
- "mkdocs.yml"
- "production-lifecycle-source.json"
- "scripts/render_production_lifecycle.py"
- "tests/test_production_lifecycle_policy.py"
push:
branches: [main]
paths:
- ".github/workflows/production-lifecycle-policy.yml"
- "docs/production-lifecycle.json"
- "docs/reference/production-lifecycle.md"
- "docs/schemas/production-lifecycle-public.schema.json"
- "mkdocs.yml"
- "production-lifecycle-source.json"
- "scripts/render_production_lifecycle.py"
- "tests/test_production_lifecycle_policy.py"

permissions:
contents: read

concurrency:
group: production-lifecycle-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
validate:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"

- name: Check exact canonical Production lifecycle projection
run: python3 scripts/render_production_lifecycle.py --check

- name: Test fail-closed projection behavior
run: python3 -m unittest tests.test_production_lifecycle_policy

report-failure:
needs: validate
if: >-
${{ always() && needs.validate.result == 'failure' &&
(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }}
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: File or update lifecycle projection failure
env:
GH_TOKEN: ${{ github.token }}
run: |
TITLE="Production lifecycle projection needs attention"
BODY="The exact-source Production lifecycle check failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}

The public projection must continue to fail closed. Fix the source pin or evidence before any consumer derives Production."
EXISTING=$(gh issue list --repo "${{ github.repository }}" --state open --search "in:title \"$TITLE\"" --json number --jq '.[0].number // empty')
if [ -n "$EXISTING" ]; then
gh issue comment "$EXISTING" --repo "${{ github.repository }}" --body "$BODY"
else
gh issue create --repo "${{ github.repository }}" --title "$TITLE" --body "$BODY"
fi
201 changes: 201 additions & 0 deletions docs/production-lifecycle.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
{
"$schema": "schemas/production-lifecycle-public.schema.json",
"derivation": {
"expired_or_revoked_latest_behavior": "no_production",
"fallback_to_older_release": false,
"mode": "latest_signed_admission_at_read_time",
"static_production_state": false
},
"maximum_admission_days": 30,
"policy_revision": 1,
"schema_version": "openadapt.public-production-lifecycle/v1",
"source": {
"files": {
"admissions": {
"path": "production-lifecycle-admissions.json",
"sha256": "sha256:aa13f9abf9c70dacdb9b13fdf237c6b5f193011ce834ac7f616ff7441c3ecbd9",
"url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/6d882bd3e5bb1e70bebaabce690800d3fe555e93/production-lifecycle-admissions.json"
},
"admissions_schema": {
"path": "schemas/production-lifecycle-admissions.schema.json",
"sha256": "sha256:a263f929549a4098244965f1e4bee73707c99173829771af2d79bedc081bdd81",
"url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/6d882bd3e5bb1e70bebaabce690800d3fe555e93/schemas/production-lifecycle-admissions.schema.json"
},
"evidence_manifest_schema": {
"path": "schemas/production-lifecycle-evidence-manifest.schema.json",
"sha256": "sha256:4bb0aff068c38ea0a407e27fac68ca9f4a178983af4f560f9dac5ebf8c672376",
"url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/6d882bd3e5bb1e70bebaabce690800d3fe555e93/schemas/production-lifecycle-evidence-manifest.schema.json"
},
"evidence_summary_schema": {
"path": "schemas/production-lifecycle-evidence-summary.schema.json",
"sha256": "sha256:01dd60e8e68391434a8bfd81a5c610f29b9d4a7ec2a36761622da141cdba7d58",
"url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/6d882bd3e5bb1e70bebaabce690800d3fe555e93/schemas/production-lifecycle-evidence-summary.schema.json"
},
"lifecycle": {
"path": "repository-lifecycle.yml",
"sha256": "sha256:dc8af3941e3f865e2303ad075a0e7f4597d601c53f5795594cd4f9bcdc1f6420",
"url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/6d882bd3e5bb1e70bebaabce690800d3fe555e93/repository-lifecycle.yml"
},
"policy": {
"path": "production-lifecycle-policy.json",
"sha256": "sha256:e1444a08ce6b16736168cce027ce9d48abb2e0e246fc0cd79c0772fa8e423e11",
"url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/6d882bd3e5bb1e70bebaabce690800d3fe555e93/production-lifecycle-policy.json"
},
"policy_schema": {
"path": "schemas/production-lifecycle-policy.schema.json",
"sha256": "sha256:7ba6daadf2bd3b4382cebc958177e709272bb360fa0a0d231f648727ed34058c",
"url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/6d882bd3e5bb1e70bebaabce690800d3fe555e93/schemas/production-lifecycle-policy.schema.json"
},
"validator": {
"path": "scripts/validate_production_lifecycle.py",
"sha256": "sha256:7f665f8c385d496aed21a580278c2af75666ea57a749ed1bb404622b383e0b5a",
"url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/6d882bd3e5bb1e70bebaabce690800d3fe555e93/scripts/validate_production_lifecycle.py"
}
},
"repository": "OpenAdaptAI/.github",
"schema_version": "openadapt.production-lifecycle-source/v1",
"source_commit": "6d882bd3e5bb1e70bebaabce690800d3fe555e93"
},
"targets": [
{
"admission_history": [],
"artifact_authority_by_kind": {
"sdist": "pypi",
"wheel": "pypi"
},
"display_name": "OpenAdapt Agent",
"id": "agent",
"latest_admission": null,
"lifecycle_scope": "repository",
"lifecycle_subject": "openadapt-agent",
"package_index_project": "openadapt-agent",
"release_kind": "public_package",
"required_artifact_kinds": [
"sdist",
"wheel"
],
"required_claim_scope": "qualified_agent_bridge_release",
"source_repository": "OpenAdaptAI/openadapt-agent"
},
{
"admission_history": [],
"artifact_authority_by_kind": {
"sdist": "pypi",
"wheel": "pypi"
},
"display_name": "OpenAdapt Capture",
"id": "capture",
"latest_admission": null,
"lifecycle_scope": "repository",
"lifecycle_subject": "openadapt-capture",
"package_index_project": "openadapt-capture",
"release_kind": "public_package",
"required_artifact_kinds": [
"sdist",
"wheel"
],
"required_claim_scope": "qualified_native_recorder_release",
"source_repository": "OpenAdaptAI/openadapt-capture"
},
{
"admission_history": [],
"artifact_authority_by_kind": {},
"display_name": "OpenAdapt Cloud",
"id": "cloud",
"latest_admission": null,
"lifecycle_scope": "repository",
"lifecycle_subject": "openadapt-cloud",
"package_index_project": null,
"release_kind": "private_deployment",
"required_artifact_kinds": [],
"required_claim_scope": "qualified_workflow_control_plane_deployment",
"source_repository": "OpenAdaptAI/openadapt-cloud"
},
{
"admission_history": [],
"artifact_authority_by_kind": {
"linux-installer": "github_release",
"macos-installer": "github_release",
"sdist": "pypi",
"wheel": "pypi",
"windows-installer": "github_release"
},
"display_name": "OpenAdapt Desktop",
"id": "desktop",
"latest_admission": null,
"lifecycle_scope": "repository",
"lifecycle_subject": "openadapt-desktop",
"package_index_project": "openadapt-desktop",
"release_kind": "public_package",
"required_artifact_kinds": [
"linux-installer",
"macos-installer",
"sdist",
"wheel",
"windows-installer"
],
"required_claim_scope": "qualified_native_workflow_desktop_release",
"source_repository": "OpenAdaptAI/openadapt-desktop"
},
{
"admission_history": [],
"artifact_authority_by_kind": {
"deployment-manifest": "managed_evidence",
"site-archive": "managed_evidence"
},
"display_name": "OpenAdapt Documentation",
"id": "docs",
"latest_admission": null,
"lifecycle_scope": "public_surface",
"lifecycle_subject": "docs.openadapt.ai",
"package_index_project": null,
"release_kind": "public_deployment",
"required_artifact_kinds": [
"deployment-manifest",
"site-archive"
],
"required_claim_scope": "production_documentation_deployment",
"source_repository": "OpenAdaptAI/openadapt-ops"
},
{
"admission_history": [],
"artifact_authority_by_kind": {
"sdist": "pypi",
"wheel": "pypi"
},
"display_name": "OpenAdapt Flow",
"id": "flow",
"latest_admission": null,
"lifecycle_scope": "repository",
"lifecycle_subject": "openadapt-flow",
"package_index_project": "openadapt-flow",
"release_kind": "public_package",
"required_artifact_kinds": [
"sdist",
"wheel"
],
"required_claim_scope": "qualified_workflow_runtime_release",
"source_repository": "OpenAdaptAI/openadapt-flow"
},
{
"admission_history": [],
"artifact_authority_by_kind": {
"sdist": "pypi",
"wheel": "pypi"
},
"display_name": "OpenAdapt",
"id": "openadapt",
"latest_admission": null,
"lifecycle_scope": "repository",
"lifecycle_subject": "OpenAdapt",
"package_index_project": "openadapt",
"release_kind": "public_package",
"required_artifact_kinds": [
"sdist",
"wheel"
],
"required_claim_scope": "qualified_workflow_launcher_release",
"source_repository": "OpenAdaptAI/OpenAdapt"
}
]
}
57 changes: 57 additions & 0 deletions docs/reference/production-lifecycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Production admission

OpenAdapt uses evidence to derive Production for an exact release. Production
is not a static repository label. A release enters the signed Production
channel only when its target-specific acceptance record passes the public
policy.

## Qualified workflow

A qualified workflow is one exact compiled workflow version that passed its
declared qualification contract on its bound execution environment. Its signed
identity binds the workflow bundle, runtime release, dependency set,
environment, input schema, policy, identity checks, effect checks, and
verification rules.

A Production runtime accepts only an exact qualified workflow identity. It
refuses an absent, expired, revoked, or mismatched qualification. A change to a
workflow version or any bound input requires a new qualification.

## Release admission

The organization policy defines seven Production targets: the launcher, Flow,
Desktop, Cloud, Capture, Agent, and this documentation deployment. Each target
has its own claim scope, release shape, artifact authorities, and evidence
adapter. Evidence for one target cannot admit another target.

An admission binds:

- the exact target and claim scope;
- the monotonic Production release identity and its predecessor;
- the release or deployment and complete artifact inventory;
- the canonical lifecycle policy and acceptance policy;
- an independently attested, remote-safe acceptance summary;
- the oracle, task count, condition count, and trial count;
- every failure-taxonomy and reliability count; and
- an immutable evidence-retention record.

The admission validator checks current PyPI metadata, immutable GitHub release
metadata, or managed-evidence object metadata. It also verifies the GitHub
artifact attestation for the acceptance summary. Healthy-path model calls,
silent incorrect success, wrong-record effects, duplicate effects, collateral
effects, and uncertain delivery must all remain zero.

## Current-state derivation

Each target keeps an append-only hash chain of signed release identities. The
highest sequence is the current Production release. If its admission expires
or is revoked, that target has no current Production release. The validator
does not fall back to an older release.

The machine-readable [Production lifecycle record](../production-lifecycle.json)
contains the exact source commit, input hashes, policy, and admission history.
It does not store a static `production: true` flag. A consumer must use the
pinned validator and derive the state at read time.

Runnable does not mean admitted. An installation, release, or successful demo
cannot create Production state without this complete evidence contract.
Loading