diff --git a/README.md b/README.md index 1ad7191..b9f9d36 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,9 @@ Organization owners should apply these settings after this branch is merged: attached to the overall `OpenAdapt` project while putting the canonical engine and Desktop cockpit beside it. 4. The Cloud implementation repository is private and cannot be a public - organization pin. The documentation implementation is Internal and remains - reachable through the public docs link instead of occupying a product pin. + organization pin. The documentation implementation is a public Support + repository and remains reachable through the docs link instead of occupying + a product pin. 5. Apply the archive queue in [REPOSITORY_LIFECYCLE.md](REPOSITORY_LIFECYCLE.md) only after each repository has an archive notice and any dirty local work is preserved. diff --git a/REPOSITORY_LIFECYCLE.md b/REPOSITORY_LIFECYCLE.md index e0d18ff..de1f38d 100644 --- a/REPOSITORY_LIFECYCLE.md +++ b/REPOSITORY_LIFECYCLE.md @@ -1,6 +1,6 @@ # OpenAdapt Repository Lifecycle Registry -Last reviewed: 2026-08-18 +Last reviewed: 2026-08-26 This public registry separates the product from experiments and records the intended lifecycle of organization repositories. It does not authorize moving @@ -15,6 +15,7 @@ The machine-readable source is [`repository-lifecycle.yml`](repository-lifecycle | Status | Meaning | |--------|---------| | **Production** | Exact latest release in the signed Production channel with an active, unexpired, non-revoked, independently attested acceptance admission | +| **Support** | Current public dependency or operational tool | | **Beta** | Active product surface with compatibility intent, but not a blanket production-readiness claim | | **Experimental** | Active prototype or optional component with no production support promise | | **Research** | Evidence-generating work, not required by the product runtime | @@ -71,30 +72,35 @@ empty for that target. The validator does not fall back to an older release. Static Production membership is forbidden. Consumers derive current Production at read time from the signed admission, its expiry, and its revocation state. -## Baseline Lifecycle +## Admission-Gated Targets -The baseline applies when a target does not have a current Production -admission. The admission overlay does not rewrite repository history. +The seven product targets do not have fallback lifecycle labels. A target that +doesn't have a current admission is **not actively admitted**. Expiry, +revocation, release drift, an authority failure, or missing evidence produces +the same state. The validator never restores an older admission or replaces +the state with Beta, Experimental, or Early access. -| Repository | Lifecycle | Role | +The signed ledger currently has no active admissions. These are the derived +states: + +| Target | Current state | Role | |------------|-----------|------| -| `OpenAdapt` | **Beta** | Launcher/meta-package and unified CLI | -| `openadapt-flow` | **Beta** | Canonical compiler and governed runtime | -| `openadapt-desktop` | **Beta** | Desktop cockpit for local recording, qualification, execution, evidence review, and governed repair | -| `openadapt-cloud` | **Beta** | Proprietary control plane for managed browser and customer-controlled execution, attended operations, evidence, usage, and billing | -| `openadapt-capture` | **Experimental** | Optional native recorder | -| `openadapt-agent` | **Beta** | Local MCP and Agent Skills bridge for compiled, governed Flow workflows | -| `openadapt-privacy` | **Experimental** | Optional scrubbing component | -| `openadapt-types` | **Experimental** | Interoperability schemas | -| `openadapt-web` | **Internal** | Marketing website implementation | - -## Research, Labs, and Internal Work +| `openadapt` | **Not actively admitted** | `OpenAdapt` launcher/meta-package and unified CLI | +| `flow` | **Not actively admitted** | `openadapt-flow` compiler and governed runtime | +| `cloud` | **Not actively admitted** | Proprietary control plane and hosted execution surface | +| `desktop` | **Not actively admitted** | Desktop recording, qualification, execution, evidence, and repair cockpit | +| `capture` | **Not actively admitted** | Native screen, input, timing, window, and media capture | +| `agent` | **Not actively admitted** | Local MCP and Agent Skills bridge for governed Flow workflows | +| `docs` | **Not actively admitted** | `docs.openadapt.ai` deployment sourced from `openadapt-ops` | + +## Other Repository Lifecycles | Group | Repositories | |-------|--------------| +| **Support** | `.github`, `openadapt-web`, `openadapt-ops`, `openadapt-wright`, `openadapt-herald`, `openadapt-crier`, `openadapt-consilium`, `openadapt-telemetry`, `openadapt-viewer`, `openadapt-blog` | +| **Experimental** | `openadapt-privacy`, `openadapt-types`, `openadapt-console`, `openadapt-tray` | | **Research** | `openadapt-ml`, `openadapt-evals`, `openadapt-retrieval`, `openadapt-grounding`, `openadapt-verifier` | -| **Internal** | `openadapt-ops`, `openadapt-wright`, `openadapt-herald`, `openadapt-crier`, `openadapt-consilium`, `openadapt-presenter`, `openadapt-bootstrap`, `openadapt-telemetry`, `openadapt-viewer`, `openadapt-blog`, `openadapt-internal`, `openadapt-yc` | -| **Experimental UI/support** | `openadapt-console`, `openadapt-tray` | +| **Internal** | `openadapt-bootstrap`, `openadapt-internal`, `openadapt-yc`, `openadapt-presenter` | | **Labs/forks** | `OmniMCP` (`omnimcp` locally), `SoM`, `PydanticPrompt` | | **Archived historical directions** | `OpenAdapter`, `OpenReflector` | | **Superseded** | `OpenSanitizer` (successor: `openadapt-privacy`) | diff --git a/profile/README.md b/profile/README.md index ab7f629..1ea751f 100644 --- a/profile/README.md +++ b/profile/README.md @@ -73,26 +73,36 @@ before extrapolating either result. ## Product Surfaces -| Surface | Lifecycle | Start here | +The signed admission ledger has no active records. Each product target is +therefore **not actively admitted**. A target shows Production only while its +exact release or deployment has an active, signed, expiring, and revocable +admission. + +| Target | Current state | Role | |---|---|---| -| **Open-source product** | **Beta** | [`OpenAdapt`](https://github.com/OpenAdaptAI/OpenAdapt) installs the unified CLI; [`openadapt-flow`](https://github.com/OpenAdaptAI/openadapt-flow) is the canonical compiler and governed runtime. | -| **Desktop cockpit** | **Beta** | [`openadapt-desktop`](https://github.com/OpenAdaptAI/openadapt-desktop) provides local recording, qualification, execution, evidence review, and governed repair. Native installers are published from its releases page. | -| **Cloud control plane** | **Beta** | [`app.openadapt.ai`](https://app.openadapt.ai/) provides managed browser execution plus workflow, evidence, attended-operation, usage, and billing surfaces for customer-controlled execution. Its implementation repository is private. | -| **Native capture** | **Experimental** | [`openadapt-capture`](https://github.com/OpenAdaptAI/openadapt-capture) is the canonical native screen, input, timing, and window-scoped recording component used by Desktop and Flow. | -| **Agent bridge** | **Beta** | [`openadapt-agent`](https://github.com/OpenAdaptAI/openadapt-agent) exposes compiled Flow workflows as local MCP tools and Agent Skills without becoming a second runtime. | -| **Documentation** | **Beta** | [`docs.openadapt.ai`](https://docs.openadapt.ai) is the canonical journey-led site; [`openadapt-ops`](https://github.com/OpenAdaptAI/openadapt-ops) is its **Internal** publishing source. | -| **Evidence and evaluation** | **Research** | [`openadapt-evals`](https://github.com/OpenAdaptAI/openadapt-evals) contains evaluation infrastructure. Runnable references live in [`openadapt-flow/docs/showcase`](https://github.com/OpenAdaptAI/openadapt-flow/tree/main/docs/showcase), with methods and evidence under [`benchmark`](https://github.com/OpenAdaptAI/openadapt-flow/tree/main/benchmark). | - -OpenAdapt is one Beta product across browser, Windows, macOS, Linux, RDP, and -Citrix/VDI. Those execution surfaces share one qualification contract while -retaining environment-specific bindings and evidence. Qualification remains -specific to the workflow, application, environment, identity contract, and -effect verifier rather than being inferred from a platform checkmark. - -Runnable is not the same as certified safe. Review identity coverage, -postconditions, system-of-record effects, policy, and the published +| `openadapt` | **Not actively admitted** | [`OpenAdapt`](https://github.com/OpenAdaptAI/OpenAdapt) installs the unified CLI. | +| `flow` | **Not actively admitted** | [`openadapt-flow`](https://github.com/OpenAdaptAI/openadapt-flow) is the canonical compiler and governed runtime. | +| `cloud` | **Not actively admitted** | [`app.openadapt.ai`](https://app.openadapt.ai/) provides the control plane for managed browser and customer-controlled execution. Its implementation repository is private. | +| `desktop` | **Not actively admitted** | [`openadapt-desktop`](https://github.com/OpenAdaptAI/openadapt-desktop) provides local recording, qualification, execution, evidence review, and governed repair. | +| `capture` | **Not actively admitted** | [`openadapt-capture`](https://github.com/OpenAdaptAI/openadapt-capture) records native screen, input, timing, and window-scoped evidence for Desktop and Flow. | +| `agent` | **Not actively admitted** | [`openadapt-agent`](https://github.com/OpenAdaptAI/openadapt-agent) exposes governed Flow workflows as local MCP tools and Agent Skills. | +| `docs` | **Not actively admitted** | [`docs.openadapt.ai`](https://docs.openadapt.ai) is the canonical documentation site. [`openadapt-ops`](https://github.com/OpenAdaptAI/openadapt-ops) is its **Support** publishing source. | + +[`openadapt-evals`](https://github.com/OpenAdaptAI/openadapt-evals) is a +Research repository. Runnable references live in +[`openadapt-flow/docs/showcase`](https://github.com/OpenAdaptAI/openadapt-flow/tree/main/docs/showcase), +with methods and evidence under +[`benchmark`](https://github.com/OpenAdaptAI/openadapt-flow/tree/main/benchmark). + +These targets form one product across browser, Windows, macOS, Linux, RDP, and +Citrix/VDI. The execution surfaces share one qualification contract and retain +environment-specific bindings and evidence. Qualification stays specific to +the workflow, application, environment, identity contract, and effect verifier. + +A runnable workflow can still lack certification. Review its identity coverage, +postconditions, system-of-record effects, policy, and published [limits](https://github.com/OpenAdaptAI/openadapt-flow/blob/main/docs/LIMITS.md) -for each consequential workflow. +before you use it for consequential work. ## Research and Labs diff --git a/repository-lifecycle.yml b/repository-lifecycle.yml index 02e56db..f4b01c0 100644 --- a/repository-lifecycle.yml +++ b/repository-lifecycle.yml @@ -1,5 +1,5 @@ schema_version: 2 -reviewed_on: 2026-08-18 +reviewed_on: 2026-08-26 canonical_product: launcher: OpenAdapt engine: openadapt-flow @@ -16,24 +16,29 @@ public_metadata: - openadapt-evals repository_descriptions: openadapt-desktop: >- - Beta Desktop cockpit for recording, qualification, local execution, - evidence, and governed repair across OpenAdapt workflows. + Desktop cockpit for recording, qualification, local execution, evidence, + and governed repair across OpenAdapt workflows. openadapt-capture: >- Native screen, input, timing, and window-scoped recording for OpenAdapt - Desktop and Flow. Experimental component. + Desktop and Flow. openadapt-agent: >- - Beta local bridge exposing governed OpenAdapt workflows as MCP tools and - Agent Skills; halts remain halts. + Local bridge exposing governed OpenAdapt workflows as MCP tools and Agent + Skills; halts remain halts. lifecycle: production: [] - beta: - - OpenAdapt - - openadapt-flow - - openadapt-cloud - - openadapt-desktop - - openadapt-agent + support: + - .github + - openadapt-web + - openadapt-ops + - openadapt-wright + - openadapt-herald + - openadapt-crier + - openadapt-consilium + - openadapt-telemetry + - openadapt-viewer + - openadapt-blog + beta: [] experimental: - - openadapt-capture - openadapt-privacy - openadapt-types - openadapt-console @@ -45,19 +50,10 @@ lifecycle: - openadapt-grounding - openadapt-verifier internal: - - openadapt-web - - openadapt-ops - - openadapt-wright - - openadapt-herald - - openadapt-crier - - openadapt-consilium - - openadapt-presenter - openadapt-bootstrap - - openadapt-telemetry - - openadapt-viewer - - openadapt-blog - openadapt-internal - openadapt-yc + - openadapt-presenter labs: - OmniMCP - SoM @@ -73,5 +69,4 @@ lifecycle: - OpenAdapter public_surfaces: production: [] - beta: - - docs.openadapt.ai + beta: [] diff --git a/scripts/check_profile.py b/scripts/check_profile.py index e381441..51d798c 100755 --- a/scripts/check_profile.py +++ b/scripts/check_profile.py @@ -3,6 +3,7 @@ from __future__ import annotations +import json import re import sys from pathlib import Path @@ -54,6 +55,7 @@ LIFECYCLE_REPOSITORY_RE = re.compile(r"^ - (\S+)$") EXPECTED_LIFECYCLE_GROUPS = { "production", + "support", "beta", "experimental", "research", @@ -65,11 +67,10 @@ "archived", } EXPECTED_CRITICAL_LIFECYCLES = { - "OpenAdapt": "beta", - "openadapt-flow": "beta", - "openadapt-desktop": "beta", - "openadapt-agent": "beta", - "openadapt-capture": "experimental", + ".github": "support", + "openadapt-web": "support", + "openadapt-ops": "support", + "openadapt-blog": "support", "OpenAdapter": "archived", "OpenReflector": "archived", } @@ -141,11 +142,44 @@ def main() -> int: errors.append(f"profile/README.md is missing required links: {missing_links}") try: - validate_files(ROOT) + active_admissions = validate_files(ROOT) except LifecycleError as exc: errors.append(f"Production lifecycle refused: {exc}") + active_admissions = {} + + policy = json.loads( + (ROOT / "production-lifecycle-policy.json").read_text(encoding="utf-8") + ) + for target in policy["targets"]: + target_id = target["id"] + state = ( + "Production" + if target_id in active_admissions + else "Not actively admitted" + ) + marker = f"| `{target_id}` | **{state}** |" + for path, text in ( + (PROFILE, profile_text), + (LIFECYCLE_DOC, LIFECYCLE_DOC.read_text(encoding="utf-8")), + ): + if marker not in text: + errors.append( + f"{path.relative_to(ROOT)} does not match the derived state for " + f"target {target_id}: {state}" + ) lifecycle_text = LIFECYCLE_DATA.read_text(encoding="utf-8") + public_metadata_text = lifecycle_text.split("lifecycle:\n", maxsplit=1)[0] + static_target_labels = sorted( + label + for label in ("Beta", "Experimental", "Early access", "Exploratory") + if re.search(rf"\b{re.escape(label)}\b", public_metadata_text, re.IGNORECASE) + ) + if static_target_labels: + errors.append( + "repository-lifecycle.yml public target metadata contains static " + f"lifecycle labels: {static_target_labels}" + ) pinned_section = lifecycle_text.split(" pinned_repositories:\n", maxsplit=1) if len(pinned_section) != 2: errors.append("repository-lifecycle.yml is missing pinned_repositories") diff --git a/scripts/validate_production_lifecycle.py b/scripts/validate_production_lifecycle.py index 0870379..85b96bf 100755 --- a/scripts/validate_production_lifecycle.py +++ b/scripts/validate_production_lifecycle.py @@ -1627,10 +1627,10 @@ def validate( for group, subjects in lifecycle.items() if group != "production" and target["lifecycle_subject"] in subjects ] - if len(memberships) != 1: + if memberships: raise LifecycleError( - f"target {target_id} must have exactly one baseline lifecycle " - f"membership; got {memberships}" + f"target {target_id} has static lifecycle membership {memberships}; " + "derive its state only from active admissions" ) return active diff --git a/tests/test_production_lifecycle.py b/tests/test_production_lifecycle.py index 6624a6e..90ab5dc 100644 --- a/tests/test_production_lifecycle.py +++ b/tests/test_production_lifecycle.py @@ -524,6 +524,45 @@ def test_registry_membership_without_admission_is_refused(self) -> None: with self.assertRaisesRegex(lifecycle.LifecycleError, "static Production"): validate_case(empty_admissions(), repositories=["openadapt-flow"]) + def test_static_repository_lifecycle_for_target_is_refused(self) -> None: + repositories, surfaces = lifecycle.load_lifecycle() + repositories = copy.deepcopy(repositories) + repositories["beta"].append("openadapt-flow") + with self.assertRaisesRegex( + lifecycle.LifecycleError, "static lifecycle membership" + ): + lifecycle.validate( + load_policy(), + empty_admissions(), + repositories, + surfaces, + policy_sha256=POLICY_DIGEST, + now=NOW, + ) + + def test_static_public_surface_lifecycle_for_target_is_refused(self) -> None: + repositories, surfaces = lifecycle.load_lifecycle() + surfaces = copy.deepcopy(surfaces) + surfaces["beta"].append("docs.openadapt.ai") + with self.assertRaisesRegex( + lifecycle.LifecycleError, "static lifecycle membership" + ): + lifecycle.validate( + load_policy(), + empty_admissions(), + repositories, + surfaces, + policy_sha256=POLICY_DIGEST, + now=NOW, + ) + + def test_current_public_support_repositories_remain_classified(self) -> None: + repositories, _surfaces = lifecycle.load_lifecycle() + self.assertGreaterEqual( + set(repositories["support"]), + {".github", "openadapt-web", "openadapt-ops", "openadapt-blog"}, + ) + def test_admission_derives_production_without_static_membership(self) -> None: admissions, _summary, remote = build_case() self.assertEqual(