diff --git a/.github/workflows/profile-consistency.yml b/.github/workflows/profile-consistency.yml new file mode 100644 index 0000000..febbe2d --- /dev/null +++ b/.github/workflows/profile-consistency.yml @@ -0,0 +1,34 @@ +name: Profile consistency + +on: + pull_request: + paths: + - ".github/workflows/profile-consistency.yml" + - "LAUNCH_PLAN.md" + - "REPOSITORY_LIFECYCLE.md" + - "profile/README.md" + - "README.md" + - "repository-lifecycle.yml" + - "scripts/check_profile.py" + push: + branches: [main] + paths: + - ".github/workflows/profile-consistency.yml" + - "LAUNCH_PLAN.md" + - "REPOSITORY_LIFECYCLE.md" + - "profile/README.md" + - "README.md" + - "repository-lifecycle.yml" + - "scripts/check_profile.py" + +permissions: + contents: read + +jobs: + validate-profile: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - name: Validate product truth and links + run: python3 scripts/check_profile.py diff --git a/LAUNCH_PLAN.md b/LAUNCH_PLAN.md new file mode 100644 index 0000000..6c465f9 --- /dev/null +++ b/LAUNCH_PLAN.md @@ -0,0 +1,134 @@ +# OpenAdapt Public Launch Plan + +Last reviewed: 2026-07-15 + +## Objective + +Launch the real hosted OpenAdapt product end to end. Development mocks may +remain available, but production execution, billing, artifact handling, and run +status must be backed by configured live services. A simulated run must never +be reported as a production success. + +## Launch Gate + +The launch candidate is accepted only when a clean account can complete this +workflow against production-like infrastructure: + +1. Create an account and organization. +2. Select and purchase the configured plan through Stripe Checkout. +3. Record or import a workflow. +4. Produce a sanitized derivative, review it when policy requires, and approve + its exact content hash. +5. Compile, lint, and certify the workflow. +6. Execute it through a real configured runner. +7. Receive authenticated callbacks and inspect the report and audit trail. +8. Halt on induced failure, teach or repair the workflow locally, validate and + activate the replacement on the same hosted workflow, and rerun it. +9. Confirm entitlement, usage, and billing state. +10. Confirm that missing production dependencies cannot fall back to simulated + success. + +## Workstreams + +| Workstream | Required outcome | +|---|---| +| Cloud mode | Explicit development/mock and production/live behavior with production dependency validation | +| Execution | Real storage, queue/runner dispatch, authenticated callbacks, reports, retries, resume, and failure recovery | +| Billing | Stripe test/live support, configured products and prices, checkout, portal, signed idempotent webhooks, entitlements, and usage metering | +| Artifact privacy | Type-complete sanitized derivatives, local review and correction, verification, manifest, and exact-hash approval | +| Runtime data | Parameters and secrets separated from templates; PHI-bearing observations retained inside the declared trusted boundary | +| Product surfaces | Website, docs, launcher, cloud, and GitHub profile describe the same shipped scope without invented claims or prices | +| Evidence | Bounded subsystem tests plus the clean-account production-like launch lifecycle | + +## Fourteen-Step Execution Plan + +1. Establish one public product description and canonical repository path. +2. Label every public component with an evidence-backed lifecycle state. +3. Freeze the launch substrate to the browser workflow until its complete gate + passes; keep other backends visibly Experimental or Research. +4. Prove clean-machine install, record, compile, lint, certify, replay, induced + drift, report inspection, and uninstall on every claimed operating system. +5. Produce sanitized derivatives from immutable source copies, inventory every + file, refuse unsupported content, rescan, review locally, and approve exact + hashes. +6. Bind runnable upload to source and bundle hashes, compiler/configuration, + parameter schema, strict lint, certification, risk class, replay evidence, + target boundary, one-time challenge, and deployment allowlists. +7. Make production mode explicit and validate every live dependency at + readiness; never substitute simulated execution when configuration is + absent. +8. Complete Stripe Checkout, portal, signed idempotent webhooks, subscription + linkage, entitlements, metering, cancellation, and refund verification. +9. Dispatch immutable bundle/version/validation snapshots to the real runner; + authenticate callbacks, verify downloaded bytes, isolate runtime parameters + and secrets, require request idempotency and single-flight dispatch, retain + ambiguous acknowledgements for operator reconciliation, reconcile only + provably abandoned work, and meter terminal outcomes exactly once. +10. Complete governed failure handling: structured halt reports, local teaching + or repair, validated replacement activation, checkpoint/resume, and rerun. +11. Publish the actual enterprise data boundary, threat model, credential and + log handling, update/rollback design, audit limitations, and regulated + deployment responsibilities. +12. Align the website, canonical journey-first docs, launcher, desktop surfaces, + pricing, organization profile, examples, and lifecycle registry. +13. Run the clean-account production lifecycle, provider probes, nightly + cross-platform lifecycle, real purchase/refund, backup/restore, incident, + and one high-value fail-closed workflow qualification; release only the + substrate that passes. +14. Publish reproducible evaluation artifacts, failure taxonomy, limitations, + and an arXiv paper after author, disclosure, licensing, sanitized-artifact, + release-identity, and evidence checks pass. + +## Artifact And PHI Contract + +- Scrubbing transforms a copy and never mutates the source artifact. +- Every file is inventoried and must have an explicit transformer or exclusion; + unknown or unsuccessfully processed content is refused. +- The sanitized derivative is rescanned and accompanied by a versioned manifest + containing source/result hashes, transformations, findings, and policy. +- Recordings and bundles containing or suspected of containing PHI require + local review by default. Review can add redactions or reject the derivative. +- Approval is bound to the exact derivative hash. A later change requires new + approval. +- Schema-minimized break reports may use an automatic policy. +- Automatic artifact approval is disabled by default. A deployment operator + may enable it only for a fully covered, reviewed sanitizer policy; an upload + request cannot enable that capability. Automatic approval must be signed by a + deployment-controlled key in an explicit allowlist over the exact artifact + and approval envelope; the ingest token alone is not sufficient. +- Sanitizing design-time artifacts does not sanitize live execution. Runtime + screenshots or observations that reintroduce PHI remain inside the declared + customer, BYOC, on-prem, or regulated managed boundary. + +## Publication Order + +1. Engine artifact policy and sanitized-derivative implementation. +2. Cloud live execution, production health, and billing. +3. Canonical product documentation and security architecture. +4. Website launch, checkout, and configured pricing surfaces. +5. Reproducible evaluation artifacts and failure taxonomy. +6. arXiv paper with methodology, identity/effect/refusal design, comparative + evidence, silent incorrect success, over-halt, limitations, and a + reproducibility checklist. Missing results must remain explicitly pending. +7. GitHub organization descriptions, pins, and lifecycle registry. +8. Repository archival and local relocation only after independent clean-tree, + credential, dependency, and worktree checks. + +## Evidence Standard + +- Comparative claims require at least three trials per task and condition. +- Results must name the task, environment, oracle, run count, failure taxonomy, + authoring effort, maintenance interventions, latency, model calls, cost, + silent incorrect success, over-halt, and recovery time. +- Mock, synthetic, analog, and real-application results must be labeled + separately. +- Code presence and a green UI are not evidence of a live remote execution. +- The arXiv paper and marketing pages may cite only checked-in, reproducible + results and must preserve the limitations disclosed by the engine. + +## Scope Control + +Launch only the backends that pass the complete gate. Other backends remain +visible with their measured maturity. Do not add repositories or unrelated +features before the launch gate passes. Do not move dirty, credential-bearing, +or registered-worktree directories into `_deprecated`. diff --git a/README.md b/README.md index a46ae92..5c8d4bc 100644 --- a/README.md +++ b/README.md @@ -1 +1,33 @@ -# .github \ No newline at end of file +# OpenAdaptAI Organization Configuration + +This repository owns the public organization profile and the lifecycle registry. +It does not change GitHub organization settings automatically. + +The cross-repository delivery sequence and acceptance criteria are maintained in +[LAUNCH_PLAN.md](LAUNCH_PLAN.md). + +## Manual GitHub Actions + +Organization owners should apply these settings after this branch is merged: + +1. Set the organization description to: `Deterministic, governed automation for repeated work trapped behind GUIs.` +2. Change the `OpenAdapt` repository description to: `Beta launcher for openadapt-flow: compile demonstrated GUI workflows into deterministic, governed local replay.` +3. Change the `openadapt-flow` description to: `Canonical OpenAdapt engine: compile demonstrated GUI workflows, replay locally without model calls on healthy runs, and govern repair and refusal.` +4. Change the `openadapt-desktop` description to: `Experimental desktop authoring and teaching surface for OpenAdapt workflows.` +5. Change the `openadapt-cloud` description to: `OpenAdapt Cloud: managed execution of locally authored, attested browser workflows, with billing and structural reports.` +6. The currently observed pins are `OpenAdapt`, `openadapt-retrieval`, + `openadapt-evals`, `openadapt-grounding`, `openadapt-capture`, and + `openadapt-privacy`. +7. Pin these five public repositories: `OpenAdapt`, `openadapt-flow`, + `openadapt-desktop`, `openadapt-ops`, and `openadapt-evals`. Leave the sixth + slot empty rather than substituting an unrelated component. The cloud source + repository is private and cannot be a public organization pin. +8. The examples surface currently lives under `openadapt-flow/docs/showcase`; + do not link or pin a nonexistent `openadapt-examples` repository. Revisit the + empty slot only after a standalone examples repository actually exists and + carries an evidence-backed lifecycle label. +9. 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. + +The current public organization tagline, `AI for Desktops.`, and several +repository descriptions are GitHub settings. Editing `profile/README.md` cannot +change them. diff --git a/REPOSITORY_LIFECYCLE.md b/REPOSITORY_LIFECYCLE.md new file mode 100644 index 0000000..21e9c4a --- /dev/null +++ b/REPOSITORY_LIFECYCLE.md @@ -0,0 +1,79 @@ +# OpenAdapt Repository Lifecycle Registry + +Last reviewed: 2026-07-15 + +This public registry separates the product from experiments and records the +intended lifecycle of organization repositories. It does not authorize moving +a local directory or archiving a GitHub repository by itself. Machine-local +checkout state and credential-response details belong in private operations +records, not in this public repository. + +The machine-readable source is [`repository-lifecycle.yml`](repository-lifecycle.yml). + +## Lifecycle Definitions + +| Status | Meaning | +|--------|---------| +| **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 | +| **Internal** | Team tooling or private strategy, not a public product surface | +| **Labs** | Standalone experiment, fork, or adjacent library; not automatically deprecated | +| **Historical** | Earlier direction retained for context; retirement still requires an explicit decision | +| **Superseded** | Functionality has a named successor; new integrations use the successor | +| **Deprecated** | Superseded; migration fixes only, no new integrations | +| **Archived** | Historical and read-only | + +## Current Product Boundary + +| Repository | Lifecycle | Role | +|------------|-----------|------| +| `OpenAdapt` | **Beta** | Launcher/meta-package and unified CLI | +| `openadapt-flow` | **Beta** | Canonical compiler and governed runtime | +| `openadapt-desktop` | **Experimental** | Desktop authoring and teaching surface | +| `openadapt-cloud` | **Beta** | Hosted browser-workflow control plane, execution, billing, structural reports, and validated replacement activation; authoring and repair remain local | +| `openadapt-capture` | **Experimental** | Optional native recorder | +| `openadapt-privacy` | **Experimental** | Optional scrubbing component | +| `openadapt-types` | **Experimental** | Interoperability schemas | +| `openadapt-web` | **Internal** | Marketing website implementation | + +## Research, Labs, and Internal Work + +| Group | Repositories | +|-------|--------------| +| **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-telemetry`, `openadapt-viewer`, `openadapt-blog`, `openadapt-internal`, `openadapt-yc` | +| **Experimental UI/support** | `openadapt-console`, `openadapt-tray` | +| **Labs/forks** | `OmniMCP` (`omnimcp` locally), `SoM`, `PydanticPrompt` | +| **Historical directions** | `OpenAdapter`, `OpenReflector` | +| **Superseded** | `OpenSanitizer` (successor: `openadapt-privacy`) | + +## Retirement Queue + +| Repository | Lifecycle | Public action | +|------------|-----------|---------------| +| `openadapt-gitbook` | **Archived** | Keep an archive notice and route documentation traffic to `docs.openadapt.ai`. | +| `openadapt-new` | **Archived** | Keep read-only historical context and route product traffic to `OpenAdapt` and `openadapt-flow`. | +| `openadapt-agent` | **Deprecated** | Add a successor notice for `openadapt-flow`; accept migration fixes only. | +| `OpenSanitizer` | **Superseded** | Add a successor notice for `openadapt-privacy`, then decide whether to archive. | +| `OpenReflector` | **Historical** | Decide Archive versus Labs before changing organization state. | +| `OpenAdapter` | **Historical** | Decide Archive versus Labs before changing organization state. | + +Experimental, Research, Labs, and Internal repositories are not deprecated by +default. Moving local checkouts is a separate operational decision that must +use private, current evidence. + +## Archive Procedure + +1. Preserve or intentionally discard every tracked and untracked local change. +2. Confirm the branch is pushed and record the final commit in private + operations evidence. +3. Replace the repository README opening with an archive notice and a link to + `openadapt-flow` or the named successor. +4. Remove active package discovery, CI, dependency, and documentation references. +5. Move the local checkout only after steps 1-4; then archive the GitHub + repository and remove it from organization pins. + +Before a separately approved local move, re-run clean-tree, upstream, dependency, +credential, destination, and registered-worktree checks without publishing +machine-specific findings here. diff --git a/profile/README.md b/profile/README.md index 9c407cd..a1746b5 100644 --- a/profile/README.md +++ b/profile/README.md @@ -1,129 +1,76 @@ # OpenAdapt.AI -**Show it once. It runs forever. On your premises.** - -OpenAdapt is an open-source **demonstration compiler** for desktop and web GUIs. -Record a workflow once, and OpenAdapt compiles it into a deterministic, -self-healing automation that replays locally at near-zero cost, verifies its own -effects, and halts rather than guessing when the screen stops matching. No API -required, no per-run model calls, and on the default path no data leaves your -machine. - -Every automation tool assumes an API. The systems that actually run regulated -work often don't: legacy EMRs, Citrix desktops, and the internal apps a team -still drives by hand. OpenAdapt learns them from a single demonstration and runs -them where your data already lives. - -- **Website:** [openadapt.ai](https://openadapt.ai/) -- **Docs:** [docs.openadapt.ai](https://docs.openadapt.ai) -- **Discord:** [join the community](https://discord.gg/yF527cQbDG) - -## Why a compiler - -For workflows you run over and over, re-reasoning through every step with a -large model is slow, expensive, and non-deterministic, and a wrong click writes -to the wrong record. OpenAdapt takes the opposite path: - -1. **Record once.** Do the task while OpenAdapt watches your screen and your - clicks. -2. **Compile.** The recording becomes a script you can read, edit, and reuse. - Each step carries a template crop, an OCR label, geometry landmarks, and - postconditions derived from what the demo changed on screen. -3. **Replay, deterministic and $0.** A resolution ladder (local match, global - match, OCR, landmark geometry, then optionally a grounding model) runs - healthy steps in milliseconds with no model calls on the hot path. -4. **Self-heal.** When the UI drifts, a lower rung re-resolves the target and - the fix lands back in the bundle as a reviewable diff. -5. **Effect-verify and halt on ambiguity.** Every run confirms what it changed - and leaves a step-by-step report; when the screen stops matching - expectations, it halts instead of guessing, and identity-verified steps - refuse to act on a low-confidence match. - -## Quick start +**Compile repeated GUI work into deterministic, governed workflows.** -```bash -pip install openadapt # CLI + demonstration compiler - -openadapt flow record --url --out rec # record a workflow once -openadapt flow compile rec --out bundle # compile it -openadapt flow replay bundle # run it, local, $0 - -openadapt flow lint bundle # report coverage gaps -openadapt flow certify bundle --policy clinical-write # enforce a safety policy -``` - -`openadapt flow ` is the recommended path; the standalone `openadapt-flow` -package on PyPI behaves identically. Compiled workflows can also be emitted as -Agent Skills or MCP servers so other agents can invoke them. - -## Maturity - -We keep this honest. - -- **Web (headless browser) path: usable today.** It runs entirely in CI with no - OS permissions, and it is the reference backend for the compiler. -- **Desktop, Citrix, and RDP backends: validating with design partners.** These - adapters are in progress, not yet production paths. -- **ML and training: research.** The demonstration-conditioned model work is a - research line, not part of the shipping compiler. - -## The product - -The supported product is the compiler and the governed runtime around it: - -| Repository | Role | -|------------|------| -| **[openadapt](https://github.com/OpenAdaptAI/OpenAdapt)** ⭐ | Install + unified CLI (`openadapt flow …`) | -| **[openadapt-flow](https://github.com/OpenAdaptAI/openadapt-flow)** | Demonstration compiler + governed runtime (replay, self-heal, effect-verify, halt-on-ambiguity, policies) | -| **[openadapt-capture](https://github.com/OpenAdaptAI/openadapt-capture)** | Optional native recorder for desktop GUI events | -| **[openadapt-privacy](https://github.com/OpenAdaptAI/openadapt-privacy)** | Optional PII/PHI scrubbing (Presidio-backed) | +OpenAdapt compiles demonstrated GUI workflows into deterministic, locally +executable programs. Healthy runs make no model calls. When interfaces drift, +OpenAdapt re-resolves from retained evidence or proposes a governed repair and +halts when verification fails. -## Research (not the product) +OpenAdapt is for repeated work trapped behind browser and desktop interfaces: +too visual or variable for brittle selectors, but too consequential to hand to +a free-form computer-use agent on every run. -These repositories explore whether human demonstrations can improve the accuracy -of general computer-use models. They are **research**, not required to record, -compile, or replay a workflow: +[Install OpenAdapt](https://github.com/OpenAdaptAI/OpenAdapt) · +[Read the docs](https://docs.openadapt.ai) · +[See current limits](https://github.com/OpenAdaptAI/openadapt-flow/blob/main/docs/LIMITS.md) · +[Visit openadapt.ai](https://openadapt.ai/) -| Repository | Focus | -|------------|-------| -| **[openadapt-ml](https://github.com/OpenAdaptAI/openadapt-ml)** | Training and inference for multimodal GUI-action models | -| **[openadapt-evals](https://github.com/OpenAdaptAI/openadapt-evals)** | Benchmark evaluation for GUI agents | -| **[openadapt-retrieval](https://github.com/OpenAdaptAI/openadapt-retrieval)** | Multimodal demonstration retrieval | -| **[openadapt-grounding](https://github.com/OpenAdaptAI/openadapt-grounding)** | UI element localization / grounding models | +## Start Locally -> **[openadapt-agent](https://github.com/OpenAdaptAI/openadapt-agent)** is -> **deprecated** and folding into `openadapt-flow`; its runtime, safety gates, -> and sessions duplicate the governed runtime that now lives in the compiler. -> -> Internal tooling (dev automation, social posts, approval bot, multi-model -> consensus, telemetry, viewer, desktop/tray shells) lives in other -> `openadapt-*` repositories. It supports development and operations and is not -> part of the compiler product. - -## Contributing - -We welcome contributions. Most product work happens in -[openadapt-flow](https://github.com/OpenAdaptAI/openadapt-flow) and -[openadapt](https://github.com/OpenAdaptAI/OpenAdapt). - -1. [Join Discord](https://discord.gg/yF527cQbDG) -2. Pick an issue from the relevant repository -3. Submit a PR (see each repository's CONTRIBUTING.md) - -## Project status - -OpenAdapt is in **active development**. The web compiler path is usable today; -desktop/Citrix/RDP backends are validating with design partners; the ML line is -research. See the [website](https://openadapt.ai/) for current benchmarks and -limits. - -## Enterprise and support - -Professional implementation and design-partner engagements are available. -Contact info@openadapt.ai, or support development via -[GitHub Sponsors](https://github.com/sponsors/OpenAdaptAI). - -## License +```bash +pip install openadapt +openadapt flow demo-record --out rec +openadapt flow compile rec --out bundle --name mockmed-triage +openadapt flow certify bundle --policy permissive +openadapt flow replay bundle --run-dir run +``` -All OpenAdapt.AI repositories are licensed under the MIT License unless otherwise -specified. See individual repository LICENSE files for details. +This runs the bundled MockMed example and writes a human-readable `REPORT.md`. +Use the [five-minute guide](https://docs.openadapt.ai/get-started/) to add lint, +policy certification, drift, repair, and deployment. + +## Six Public Surfaces + +| Surface | Lifecycle | Start here | +|---|---|---| +| **Engine** | **Beta** | [`openadapt-flow`](https://github.com/OpenAdaptAI/openadapt-flow) is the canonical demonstration compiler and governed runtime. | +| **Desktop authoring** | **Experimental** | [`openadapt-desktop`](https://github.com/OpenAdaptAI/openadapt-desktop) is the local recording and teaching interface. Native release artifacts are not yet publicly available. | +| **Hosted browser workflows** | **Beta** | [`app.openadapt.ai`](https://app.openadapt.ai/) provides managed browser recording, execution, billing, usage, and structural reports; its implementation repository is private. | +| **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. | +| **Evaluation** | **Research** | [`openadapt-evals`](https://github.com/OpenAdaptAI/openadapt-evals) contains evaluation infrastructure. Research results do not expand product maturity by implication. | +| **Examples** | **Beta** | Runnable examples currently 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). There is no standalone `openadapt-examples` repository today. | + +`OpenAdapt` is the Beta launcher and unified CLI; `openadapt-flow` remains the +engine. Browser record, compile, replay, reporting, and bounded deterministic +repair are the reference path. Desktop and remote-display backends retain their +separate Experimental or Research labels. + +Runnable is not the same as certified safe. Review identity coverage, +postconditions, system-of-record effects, policy, and the published +[limits](https://github.com/OpenAdaptAI/openadapt-flow/blob/main/docs/LIMITS.md) +for each consequential workflow. + +## Research and Labs + +Model training, retrieval, grounding, and general computer-use work remain +Research: [`openadapt-ml`](https://github.com/OpenAdaptAI/openadapt-ml), +[`openadapt-retrieval`](https://github.com/OpenAdaptAI/openadapt-retrieval), and +[`openadapt-grounding`](https://github.com/OpenAdaptAI/openadapt-grounding). +They are not required for healthy deterministic replay. + +OmniMCP, SoM, and PydanticPrompt are **Labs**, not product dependencies. +Historical, Superseded, Deprecated, Archived, and Internal repositories are +classified in the public +[lifecycle registry](https://github.com/OpenAdaptAI/.github/blob/main/REPOSITORY_LIFECYCLE.md) +rather than presented as the product. + +## Contribute + +Product-engine changes belong in +[`openadapt-flow`](https://github.com/OpenAdaptAI/openadapt-flow). Packaging and +launcher changes belong in [`OpenAdapt`](https://github.com/OpenAdaptAI/OpenAdapt). +Use each repository's issues for scoped work, or visit +[`openadapt.ai`](https://openadapt.ai/) for deployment inquiries. + +Unless a repository says otherwise, OpenAdapt.AI code is MIT licensed. diff --git a/repository-lifecycle.yml b/repository-lifecycle.yml new file mode 100644 index 0000000..1146813 --- /dev/null +++ b/repository-lifecycle.yml @@ -0,0 +1,50 @@ +schema_version: 1 +reviewed_on: 2026-07-15 +canonical_product: + launcher: OpenAdapt + engine: openadapt-flow +lifecycle: + beta: + - OpenAdapt + - openadapt-flow + - openadapt-cloud + experimental: + - openadapt-desktop + - openadapt-capture + - openadapt-privacy + - openadapt-types + - openadapt-console + - openadapt-tray + research: + - openadapt-ml + - openadapt-evals + - openadapt-retrieval + - openadapt-grounding + - openadapt-verifier + internal: + - openadapt-web + - openadapt-ops + - openadapt-wright + - openadapt-herald + - openadapt-crier + - openadapt-consilium + - openadapt-presenter + - openadapt-telemetry + - openadapt-viewer + - openadapt-blog + - openadapt-internal + - openadapt-yc + labs: + - OmniMCP + - SoM + - PydanticPrompt + historical: + - OpenReflector + - OpenAdapter + superseded: + - OpenSanitizer + deprecated: + - openadapt-agent + archived: + - openadapt-new + - openadapt-gitbook diff --git a/scripts/check_profile.py b/scripts/check_profile.py new file mode 100644 index 0000000..467dc50 --- /dev/null +++ b/scripts/check_profile.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +"""Check the public profile without making network requests.""" + +from __future__ import annotations + +import re +import sys +from pathlib import Path +from urllib.parse import unquote, urlsplit + + +ROOT = Path(__file__).resolve().parents[1] +PROFILE = ROOT / "profile" / "README.md" +LIFECYCLE_DOC = ROOT / "REPOSITORY_LIFECYCLE.md" +LIFECYCLE_DATA = ROOT / "repository-lifecycle.yml" +MARKDOWN_FILES = (ROOT / "README.md", ROOT / "LAUNCH_PLAN.md", LIFECYCLE_DOC, PROFILE) +CANONICAL_TRUTH = ( + "OpenAdapt compiles demonstrated GUI workflows into deterministic, locally " + "executable programs. Healthy runs make no model calls. When interfaces " + "drift, OpenAdapt re-resolves from retained evidence or proposes a governed " + "repair and halts when verification fails." +) +REQUIRED_PROFILE_LINKS = { + "https://github.com/OpenAdaptAI/OpenAdapt", + "https://github.com/OpenAdaptAI/openadapt-flow", + "https://github.com/OpenAdaptAI/openadapt-desktop", + "https://github.com/OpenAdaptAI/openadapt-ops", + "https://github.com/OpenAdaptAI/openadapt-evals", + "https://github.com/OpenAdaptAI/openadapt-flow/tree/main/docs/showcase", + "https://openadapt.ai/", + "https://app.openadapt.ai/", + "https://docs.openadapt.ai", +} +REQUIRED_PROFILE_MARKERS = { + "## Six Public Surfaces", + "## Research and Labs", + "There is no standalone `openadapt-examples` repository today.", +} +LINK_RE = re.compile(r"!?\[[^\]]+\]\(([^\s)]+)(?:\s+[^)]*)?\)") +LIFECYCLE_GROUP_RE = re.compile(r"^ ([a-z_]+):$") +LIFECYCLE_REPOSITORY_RE = re.compile(r"^ - (\S+)$") +EXPECTED_LIFECYCLE_GROUPS = { + "beta", + "experimental", + "research", + "internal", + "labs", + "historical", + "superseded", + "deprecated", + "archived", +} +FORBIDDEN_PUBLIC_OPERATIONS_MARKERS = ( + "/Users/", + "~/", + "dirty_worktree", + "possible_credentials", + ".pem", + "accessKeys", +) + + +def check_link(source: Path, destination: str) -> str | None: + destination = destination.strip("<>") + parsed = urlsplit(destination) + + if parsed.scheme in {"http", "https"}: + if not parsed.netloc or parsed.username or parsed.password: + return f"invalid external URL: {destination}" + if "utm_" in parsed.query.lower(): + return f"tracking parameters are not allowed: {destination}" + return None + + if parsed.scheme == "mailto": + return None if "@" in parsed.path else f"invalid email link: {destination}" + if parsed.scheme: + return f"unsupported link scheme: {destination}" + if not parsed.path: + return None if parsed.fragment else "empty link destination" + + target = (source.parent / unquote(parsed.path)).resolve() + try: + target.relative_to(ROOT) + except ValueError: + return f"local link escapes the repository: {destination}" + if not target.exists(): + return f"missing local target: {destination}" + return None + + +def main() -> int: + errors: list[str] = [] + profile_text = PROFILE.read_text(encoding="utf-8") + normalized_profile = " ".join(profile_text.split()) + + if CANONICAL_TRUTH not in normalized_profile: + errors.append("profile/README.md is missing the canonical product truth") + + for marker in REQUIRED_PROFILE_MARKERS: + if marker not in profile_text: + errors.append(f"profile/README.md is missing required marker: {marker}") + + profile_links = set(LINK_RE.findall(profile_text)) + missing_links = sorted(REQUIRED_PROFILE_LINKS - profile_links) + if missing_links: + errors.append(f"profile/README.md is missing required links: {missing_links}") + + lifecycle_text = LIFECYCLE_DATA.read_text(encoding="utf-8") + lifecycle_section = lifecycle_text.split("lifecycle:\n", maxsplit=1) + if len(lifecycle_section) != 2: + errors.append("repository-lifecycle.yml is missing its lifecycle mapping") + else: + groups: dict[str, list[str]] = {} + active_group: str | None = None + for line in lifecycle_section[1].splitlines(): + if match := LIFECYCLE_GROUP_RE.fullmatch(line): + active_group = match.group(1) + groups[active_group] = [] + elif match := LIFECYCLE_REPOSITORY_RE.fullmatch(line): + if active_group is None: + errors.append( + "repository-lifecycle.yml has a repository outside a lifecycle group" + ) + else: + groups[active_group].append(match.group(1)) + elif line and not line.startswith(" "): + break + + if set(groups) != EXPECTED_LIFECYCLE_GROUPS: + errors.append( + "repository-lifecycle.yml lifecycle groups do not match the public schema" + ) + repositories = [repository for values in groups.values() for repository in values] + duplicates = sorted( + repository for repository in set(repositories) if repositories.count(repository) > 1 + ) + if duplicates: + errors.append( + f"repository-lifecycle.yml assigns multiple lifecycles: {duplicates}" + ) + + public_operations_text = lifecycle_text + LIFECYCLE_DOC.read_text(encoding="utf-8") + leaked_markers = sorted( + marker + for marker in FORBIDDEN_PUBLIC_OPERATIONS_MARKERS + if marker in public_operations_text + ) + if leaked_markers: + errors.append( + "public lifecycle registry contains machine-local or credential-response " + f"details: {leaked_markers}" + ) + + for source in MARKDOWN_FILES: + text = source.read_text(encoding="utf-8") + links = LINK_RE.findall(text) + if text.count("](") != len(links): + errors.append(f"{source.relative_to(ROOT)} has malformed Markdown link syntax") + for destination in links: + if error := check_link(source, destination): + errors.append(f"{source.relative_to(ROOT)}: {error}") + + if errors: + for error in errors: + print(f"ERROR: {error}", file=sys.stderr) + return 1 + + link_count = sum( + len(LINK_RE.findall(path.read_text(encoding="utf-8"))) + for path in MARKDOWN_FILES + ) + print(f"Validated canonical product truth and {link_count} Markdown links.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())