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

on:
pull_request:
paths:
- README.md
- production-lifecycle-source.json
- scripts/render_readme_maturity.py
- tests/test_readme_maturity.py
- .github/workflows/production-lifecycle.yml
push:
branches: [main]
paths:
- README.md
- production-lifecycle-source.json
- scripts/render_readme_maturity.py
- tests/test_readme_maturity.py
- .github/workflows/production-lifecycle.yml
schedule:
- cron: "23 7 * * *"
workflow_dispatch:
repository_dispatch:
types: [production-lifecycle-changed]

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

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
- name: Prove the consumer contract
run: python -m unittest discover -s tests -p test_readme_maturity.py
- name: Validate the exact source and README projection
run: >-
python scripts/render_readme_maturity.py
--check
--require-current-source

report-drift:
needs: validate
if: >-
${{ always() && needs.validate.result == 'failure' &&
(github.event_name == 'schedule' ||
github.event_name == 'workflow_dispatch' ||
github.event_name == 'repository_dispatch') }}
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Open or update the lifecycle drift issue
env:
GH_TOKEN: ${{ github.token }}
TITLE: "README Production lifecycle source has drifted"
BODY: >-
The signed Production lifecycle consumer refused the current source
or README projection. Review the exact failure at
${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}.
run: |
set -euo pipefail
EXISTING=$(gh issue list --repo "${GITHUB_REPOSITORY}" --state open \
--search "in:title \"${TITLE}\"" --json number,title \
--jq '[.[] | select(.title == env.TITLE)][0].number // empty')
if [ -n "${EXISTING}" ]; then
gh issue edit "${EXISTING}" --repo "${GITHUB_REPOSITORY}" --body "${BODY}"
else
gh issue create --repo "${GITHUB_REPOSITORY}" --title "${TITLE}" --body "${BODY}"
fi
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Repository role

This public repository is the Beta launcher/meta-package and stable community
This public repository is the launcher/meta-package and stable community
entry point for OpenAdapt. It owns `pip install openadapt`, the unified
`openadapt` CLI, release compatibility, and launcher packaging.

Expand Down
47 changes: 40 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,21 @@ unavoidable and the outcome needs proof.
> and governed runtime are implemented in
> [`openadapt-flow`](https://github.com/OpenAdaptAI/openadapt-flow). This
> repository provides the unified `openadapt` CLI and compatibility surface,
> not a second engine. Lifecycle: **Beta**.
> not a second engine.

<!-- BEGIN PRODUCTION LIFECYCLE -->
> **Built for qualified production workflows.** A Production run requires both
> active signed product admissions for the exact OpenAdapt component and
> deployment releases, and an active signed, expiring, revocable workflow
> admission for the exact compiled workflow version. The workflow admission binds
> the organization and workflow identity; bundle version and digest; admitted
> runtime release; application and environment; input, action, identity, effect,
> and policy contracts; evidence authority; and its issue, expiry, and revocation
> state. Qualification requires at least three trials for each task and condition.
> A closed result schema must report silent incorrect success and over-halt. Any
> bound change requires a new qualification.
> [Check the live signed Production record](https://docs.openadapt.ai/production-lifecycle.json).
<!-- END PRODUCTION LIFECYCLE -->

## Try it locally

Expand Down Expand Up @@ -126,6 +140,24 @@ state, record identity, target uniqueness, and the fresh application view.
Afterward it waits for settled state and evaluates the declared effect. If the
contract cannot be established, it returns evidence and halts.

### Complete run outcomes

Every terminal run records what the runtime knows about the business effect:

| Outcome | Meaning |
|---|---|
| `VERIFIED` | Every declared effect and collateral-effect check passed at the required evidence tier. This is the only production success. |
| `HALTED_BEFORE_EFFECT` | The run stopped and positive evidence established that no consequential effect occurred. |
| `RECONCILIATION_REQUIRED` | Delivery or persistence is uncertain, conflicting, or temporarily unverifiable. The runtime never blind-retries it. |
| `FAILED_PLATFORM` | An OpenAdapt platform failure occurred before any possible business effect. |
| `CANCELED` | The run was canceled before any business effect. |
| `REJECTED_POLICY` | Authorization, identity, qualification, or environment policy refused execution before any effect. |
| `COMPLETED_UNVERIFIED` | A Demo run completed without production-grade effect evidence. |
| `ROLLED_BACK` | A detected duplicate or collateral write was compensated and re-verified. |

These terminal outcomes are not interchangeable. A resumed
`RECONCILIATION_REQUIRED` run must first reacquire and reconcile the live state.

### A focused question when a person is needed

A halted run can send one signed task to the OpenAdapt phone view. The task can
Expand Down Expand Up @@ -175,14 +207,15 @@ Every workflow is qualified against its exact application, version,
environment, identity contract, and effect verifier rather than inheriting a
blanket platform claim.

Substrate maturity, stated the same way across the OpenAdapt repositories:
Each qualification binds the exact surface, application, version, environment,
identity contract, and effect verifier:

| Substrate | Maturity |
| Substrate | Evidence available to a qualified workflow |
| --- | --- |
| Browser (web) | Beta; available in production today through the managed browser product |
| Native desktop (Windows, macOS, Linux) | Available for customer-controlled execution; qualification evidence is task- and environment-specific |
| Remote display (RDP) | Available for customer-controlled execution; qualification evidence is task- and environment-specific |
| Citrix / VDI | Available for customer-controlled execution; real-environment ICA/HDX qualification is deployment-specific |
| Browser (web) | DOM, accessibility, visual, OCR, field geometry, and source-time secret exclusion |
| Native desktop (Windows, macOS, Linux) | Visual, OCR, and local window scope, plus adapter-supplied UI Automation, Accessibility, or AT-SPI evidence when present |
| Remote display (RDP) | External pixels, OCR, anchors, keyboard, mouse, and fresh-frame verification |
| Citrix / VDI | External pixels, OCR, anchors, keyboard, mouse, and deployment-bound verification |

See the [substrate model](https://docs.openadapt.ai/concepts/substrate-model/),
[qualification evidence](https://docs.openadapt.ai/get-started/what-works-today/),
Expand Down
20 changes: 13 additions & 7 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,19 @@ not make a recording safe to upload. A derivative crosses a boundary only
after local sanitization, complete inventory, review, exact-hash approval, and
destination policy checks.

## Maturity boundary

The launcher and Flow engine are Beta. Browser workflows run through the
complete clean-machine lifecycle on Linux, macOS, and Windows. Native and
remote evidence is bounded to named tasks and environments. Citrix is
code-qualified and requires a live deployment qualification. No repository
status certifies an arbitrary customer workflow.
## Production qualification

Production applies to exact admitted product and deployment releases and an
exact qualified workflow, not to a substrate name. The workflow admission binds
the organization and workflow identity; bundle version and digest; admitted
runtime release; application and environment; input, action, identity, effect,
and policy contracts; evidence authority; and its issue, expiry, and revocation
state. Qualification requires at least three trials for each task and condition.
A closed result schema must report silent incorrect success and over-halt. A
Production run gate must reject an absent, expired, revoked, or mismatched
product or workflow admission. The
[live signed Production record](https://docs.openadapt.ai/production-lifecycle.json)
is the maturity authority for public releases.

The former model-training architecture remains in Git history and optional
research packages. It is not the current product architecture.
16 changes: 9 additions & 7 deletions docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,12 @@ record time. Read the
[canonical recording guide](https://docs.openadapt.ai/guides/record-your-app/)
before a real-data demonstration.

## Product boundary

The launcher and Flow engine are Beta. Browser workflows run end to end in the
three-OS clean-machine lifecycle. Native and remote evidence is task- and
environment-specific. Citrix support is code-qualified and still requires a
real deployment qualification. A runnable workflow is not automatically a
certified production workflow.
## Production qualification

A runnable workflow is not automatically a qualified production workflow. A
qualification binds the exact application, version, environment, input schema,
identity checks, effect checks, policy, and verification rules. A Production
run gate must reject an absent, expired, revoked, or mismatched qualification.
Check the
[live signed Production record](https://docs.openadapt.ai/production-lifecycle.json)
for the current admitted releases.
5 changes: 3 additions & 2 deletions docs/packages/capture.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# openadapt-capture

**Lifecycle: Experimental**, as recorded in the canonical
[organization lifecycle registry](https://github.com/OpenAdaptAI/.github/blob/main/REPOSITORY_LIFECYCLE.md).
`openadapt-capture` is the component OpenAdapt uses for native screen, mouse,
keyboard, timing, window-scope, and media capture.

An exact Capture release can receive a Production admission only through the
[live signed Production record](https://docs.openadapt.ai/production-lifecycle.json).

Repository:
[`OpenAdaptAI/openadapt-capture`](https://github.com/OpenAdaptAI/openadapt-capture)

Expand Down
8 changes: 4 additions & 4 deletions docs/packages/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ cockpit, privacy tools, and optional managed control plane.

| Package | Current role | Install route |
| --- | --- | --- |
| `openadapt` | Beta launcher and unified CLI | `pip install openadapt` |
| `openadapt-flow` | Beta compiler and governed runtime; installed by the launcher | `pip install openadapt-flow` for engine-only use |
| `openadapt-capture` | Experimental native capture component | `pip install 'openadapt[capture]'` |
| `openadapt` | Launcher and unified CLI | `pip install openadapt` |
| `openadapt-flow` | Compiler and governed runtime; installed by the launcher | `pip install openadapt-flow` for engine-only use |
| `openadapt-capture` | Native capture component | `pip install 'openadapt[capture]'` |
| `openadapt-privacy` | Local privacy and sanitization support | `pip install 'openadapt[privacy]'` |
| `openadapt-desktop` | Beta visual authoring and operator application | [Download an installer](https://openadapt.ai/download) |
| `openadapt-desktop` | Visual authoring and operator application | [Download an installer](https://openadapt.ai/download) |

Install the browser tutorial path:

Expand Down
21 changes: 11 additions & 10 deletions docs/platform-compatibility-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
<!-- Generated by scripts/render_platform_versions.py. Do not edit. -->

- Generated: `2026-08-20T15:18:32+00:00`
- Release channel: **beta**
- Selection: `latest-published`
- Compatibility: **dependency-compatible**
- Signature: `unsigned (signing infrastructure pending)`
Expand Down Expand Up @@ -67,15 +66,17 @@ These edges show whether each selected integration version satisfies its publish

## Substrate delivery

| Substrate | Public label | Delivery |
|---|---|---|
| Browser | Available | Local, managed OpenAdapt Cloud, or customer-controlled |
| Windows | Available | Local or customer-controlled |
| macOS | Available | Local or customer-controlled |
| Linux | Available | Local or customer-controlled |
| RDP | Available | Local or customer-controlled |
| Citrix / VDI | Available | Local or customer-controlled |
| Hosted Cloud | Beta | Managed browser runner and control plane |
Compatibility and delivery facts do not assign lifecycle state. Use the signed Production record for release maturity.

| Substrate | Delivery |
|---|---|
| Browser | Local, managed OpenAdapt Cloud, or customer-controlled |
| Windows | Local or customer-controlled |
| macOS | Local or customer-controlled |
| Linux | Local or customer-controlled |
| RDP | Local or customer-controlled |
| Citrix / VDI | Local or customer-controlled |
| Hosted Cloud | Managed browser runner and control plane |

## Qualification evidence

Expand Down
2 changes: 1 addition & 1 deletion docs/platform-manifest.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ Top-level fields:
| `schema_version` | Semver of this schema; validators reject unknown majors. |
| `generated_at` | UTC timestamp of generation. |
| `generation` | SHA-256 bindings for the generator and report renderer. |
| `release_channel` | Lowercased product lifecycle from status.json (currently `beta`). |
| `release_channel` | Legacy artifact-channel compatibility field copied from status.json. It is not a lifecycle authority. Consumers use the signed Production record for release maturity. |
| `release_selection` | Latest-published selection or exact published version inputs supplied for an ordered release train. |
| `components` | `launcher`, `flow`, `capture`, `privacy`, `types`, `desktop`, and `agent`: package name, published version, Python range, dependency constraints and markers, release source commit/tree, and the exact artifact set. |
| `runtime_units` | Launcher, runner, Desktop, and Agent deployment views. Non-locked units name selected packages. The Desktop sidecar names the exact lock-resolved closure. |
Expand Down
4 changes: 2 additions & 2 deletions openadapt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
research packages:

pip install openadapt # launcher + openadapt-flow
pip install openadapt[capture] # experimental native capture
pip install openadapt[privacy] # experimental privacy scrubbing
pip install openadapt[capture] # native capture
pip install openadapt[privacy] # privacy scrubbing
pip install openadapt[ml,evals] # research toolkits
"""

Expand Down
2 changes: 1 addition & 1 deletion openadapt/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def list_commands(self, ctx):
@click.group(cls=_FlowFirstGroup)
@click.version_option(version=__version__, prog_name="openadapt")
def main():
"""OpenAdapt - Beta launcher for the openadapt-flow compiler.
"""OpenAdapt launcher for the openadapt-flow compiler.

Compile a demonstrated workflow into deterministic local replay. Healthy
runs make no model calls; configured checks can halt on ambiguity. Native
Expand Down
2 changes: 1 addition & 1 deletion platform-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
{
"path": "scripts/render_platform_versions.py",
"sha256": "d63fa4d56e219551be2d416f5c4fea62c49c6c515fcf26183475ec4a1587808e"
"sha256": "ae3e52b501e6f5fcf497e1aa3973478a333b3fefe6dd8b9b82514e8f570bdd1f"
}
]
},
Expand Down
17 changes: 17 additions & 0 deletions production-lifecycle-source.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"schema_version": "openadapt.production-readme-source/v1",
"repository": "OpenAdaptAI/openadapt-ops",
"source_commit": "ef29c08ad27b05637abc75fc3ceb3e026821fdda",
"files": {
"projection": {
"path": "docs/production-lifecycle.json",
"url": "https://raw.githubusercontent.com/OpenAdaptAI/openadapt-ops/ef29c08ad27b05637abc75fc3ceb3e026821fdda/docs/production-lifecycle.json",
"sha256": "sha256:6c3819c7c68acb1bea8045fa1f059721e535c960707998b8705438f8541fb3d7"
},
"schema": {
"path": "docs/schemas/production-lifecycle-public.schema.json",
"url": "https://raw.githubusercontent.com/OpenAdaptAI/openadapt-ops/ef29c08ad27b05637abc75fc3ceb3e026821fdda/docs/schemas/production-lifecycle-public.schema.json",
"sha256": "sha256:f681a9bb47ff727b409df994e69e62e7c45b38e912911af4c99eca7dd3be40eb"
}
}
}
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ keywords = [
"citrix",
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
Expand Down
15 changes: 6 additions & 9 deletions scripts/render_platform_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ def render_markdown(manifest: dict) -> str:
"<!-- Generated by scripts/render_platform_versions.py. Do not edit. -->",
"",
f"- Generated: `{manifest.get('generated_at')}`",
f"- Release channel: **{manifest.get('release_channel')}**",
f"- Selection: `{selection.get('mode')}`",
f"- Compatibility: **{status.get('status')}**",
f"- Signature: `{manifest.get('signature', {}).get('status')}`",
Expand Down Expand Up @@ -175,15 +174,15 @@ def render_markdown(manifest: dict) -> str:
"",
"## Substrate delivery",
"",
"| Substrate | Public label | Delivery |",
"|---|---|---|",
"Compatibility and delivery facts do not assign lifecycle state. "
"Use the signed Production record for release maturity.",
"",
"| Substrate | Delivery |",
"|---|---|",
]
)
for substrate in manifest.get("substrate_drivers", []):
lines.append(
f"| {substrate.get('name')} | {substrate.get('public_label')} | "
f"{substrate.get('delivery')} |"
)
lines.append(f"| {substrate.get('name')} | {substrate.get('delivery')} |")

lines.extend(["", "## Qualification evidence", ""])
for evidence in manifest.get("qualification_evidence", []):
Expand Down Expand Up @@ -220,7 +219,6 @@ def main() -> int:
if args.format == "json":
rendered = json.dumps(
{
"release_channel": manifest.get("release_channel"),
"compatibility_status": manifest.get("compatibility_status", {}).get(
"status"
),
Expand All @@ -234,7 +232,6 @@ def main() -> int:
rendered = render_markdown(manifest)
else:
text_lines = [
f"Release channel: {manifest.get('release_channel')}",
f"Compatibility: {manifest.get('compatibility_status', {}).get('status')}",
]
for row in rows:
Expand Down
Loading