Skip to content
Closed
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
12 changes: 12 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Default owner for everything in the repo.
* @abrichr

# Safety-critical core: identity gate, resolution ladder, halt/postcondition
# logic. Changes here warrant extra scrutiny (never-false-accept invariant).
/openadapt_flow/runtime/identity.py @abrichr
/openadapt_flow/runtime/replayer.py @abrichr
/openadapt_flow/validation/ @abrichr

# Release, CI, and supply-chain configuration.
/.github/ @abrichr
/pyproject.toml @abrichr
46 changes: 46 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Bug report
description: Report something that behaves incorrectly.
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for the report. Please do **not** file security issues here —
see [SECURITY.md](https://github.com/OpenAdaptAI/openadapt-flow/blob/main/SECURITY.md).
- type: textarea
id: what-happened
attributes:
label: What happened?
description: What did you expect, and what happened instead?
validations:
required: true
- type: textarea
id: repro
attributes:
label: Steps to reproduce
description: Ideally the exact `openadapt-flow` commands you ran.
placeholder: |
1. openadapt-flow demo-record --out rec
2. openadapt-flow compile rec --out bundle --name my-task
3. openadapt-flow replay bundle
validations:
required: true
- type: textarea
id: report
attributes:
label: Run report / logs
description: Attach the run's REPORT.md or report.json if you have one (scrub any PHI/PII first).
- type: input
id: version
attributes:
label: openadapt-flow version
description: "Output of: python -c \"import openadapt_flow; print(openadapt_flow.__version__)\""
validations:
required: true
- type: input
id: env
attributes:
label: OS + Python version
placeholder: "macOS 14 / Python 3.12"
validations:
required: true
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Security vulnerability
url: https://github.com/OpenAdaptAI/openadapt-flow/security/advisories/new
about: Please report security issues privately, not as public issues (see SECURITY.md).
- name: Question / discussion
url: https://github.com/OpenAdaptAI/openadapt-flow/discussions
about: Ask usage questions or discuss ideas here.
21 changes: 21 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Feature request
description: Suggest an improvement or new capability.
labels: ["enhancement"]
body:
- type: textarea
id: problem
attributes:
label: Problem / use case
description: What are you trying to do, and what makes it hard today?
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed solution
description: What would you like to see? Alternatives you considered?
- type: textarea
id: context
attributes:
label: Additional context
description: Substrate (browser/desktop/RDP), scale, determinism/safety needs, etc.
27 changes: 27 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## What & why

<!-- What does this change and why? Link any issue: Closes #123 -->

## Type of change

<!-- Conventional Commit type; the PR title MUST match (feat:/fix:/docs:/…). -->

- [ ] `fix:` bug fix
- [ ] `feat:` new feature
- [ ] `docs:` documentation only
- [ ] `ci:` / `chore:` / `refactor:` / `test:` (no user-facing behavior change)

## Checklist

- [ ] PR title uses Conventional Commit format
- [ ] `ruff check openadapt_flow` and `ruff format --check openadapt_flow` pass
- [ ] `mypy` passes
- [ ] `pytest -q` passes locally
- [ ] Tests added/updated for behavior changes
- [ ] Docs updated (README/DESIGN/docs) if behavior or contracts changed
- [ ] If this touches the identity gate / resolution ladder / halt logic, I
explained why the never-false-accept invariant still holds

## Notes for reviewers

<!-- Anything that helps review: tradeoffs, follow-ups, out-of-scope items. -->
27 changes: 27 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Dependabot: keep the supply chain fresh. GitHub Actions are pinned by commit
# SHA (see the workflows); Dependabot understands the trailing `# vX` comment
# and proposes SHA bumps with the new version, so pinning does not freeze us on
# stale, potentially-vulnerable action releases.
version: 2
updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
groups:
actions:
patterns: ["*"]
commit-message:
prefix: ci

- package-ecosystem: pip
directory: "/"
schedule:
interval: weekly
# Group the minor/patch churn so we get one PR instead of a swarm; majors
# come as their own reviewable PRs.
groups:
python-minor:
update-types: ["minor", "patch"]
commit-message:
prefix: build
208 changes: 200 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,40 +1,232 @@
name: CI

# Triggers are deliberately asymmetric to keep PR feedback fast and cheap:
# - pull_request: run the FAST required gate (lint + unit `test`) on every PR.
# - push to main: re-validate main AND run the heavy post-merge suites
# (full Python matrix + macOS + windows-mock + wheel + slow e2e/browser).
# - schedule: a nightly run of the same heavy suites, so long-lived branches
# and environment drift are caught even without a merge.
# There is intentionally NO bare `push:` -- a bare push trigger fires a SECOND
# identical run alongside `pull_request` on every PR-branch push (double the
# runner spend for zero extra signal).
on:
push:
pull_request:
push:
branches: [main]
schedule:
- cron: "0 7 * * *" # 07:00 UTC nightly full matrix

# Cancel any superseded in-flight run for the same ref (e.g. a force-push or a
# rapid second push to a PR branch) instead of letting both run to completion.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

# GitHub Actions are pinned to full commit SHAs (not mutable tags) as a
# supply-chain control; the trailing comment records the human-readable
# version. Dependabot (.github/dependabot.yml) proposes SHA bumps.

jobs:
# --- Lint + format + type-check (fast, single interpreter) ---------------
# Runs on every event. On PRs it is chained INTO the merge gate: `test`
# `needs: lint`, so a lint failure skips `test` and the required check never
# goes green -> the PR cannot merge with failing lint.
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml

- name: Install
run: pip install -e .[dev]

- name: Ruff lint
run: ruff check openadapt_flow

- name: Ruff format check
run: ruff format --check openadapt_flow

- name: Mypy
run: mypy

# --- Required merge gate: FAST unit suite (ubuntu, single Python) ---------
# This is the ONE required status check (branch protection context: "test").
# It MUST stay a single, non-matrix job named exactly `test` so its reported
# context is exactly `test` -- a matrix would report `test (3.12)` etc. and
# the required `test` check would never report, permanently blocking merges.
# It runs the bulk of the unit suite but EXCLUDES the slow end-to-end
# browser/OCR record->compile->replay suite (tests/e2e), which is exercised
# post-merge by `test-matrix`. Keeps PR feedback quick.
test:
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- uses: actions/setup-python@v5
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml

- name: Install
run: pip install -e .[dev]

# Cache the Playwright browser binaries (chromium) so the lightweight
# browser-backed unit tests don't re-download ~150MB every run. Keyed on
# the resolved playwright version so a dependency bump busts the cache.
- name: Resolve Playwright version
id: pw
run: echo "version=$(python -c 'import importlib.metadata as m; print(m.version(\"playwright\"))')" >> "$GITHUB_OUTPUT"

- name: Cache Playwright browsers
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ steps.pw.outputs.version }}

- name: Install Playwright browser
run: playwright install --with-deps chromium

# --basetemp pins pytest's tmp_path factory inside the workspace so
# run dirs (REPORT.md + step/heal PNGs) survive for artifact upload;
# by default they land in the system temp dir and are unrecoverable
# when a drift/heal test fails in CI.
- name: Test
run: mkdir -p runs && pytest -q --basetemp=runs/ci
# when a drift/heal test fails in CI. --cov reports coverage as a
# visibility number (no hard floor yet). --ignore=tests/e2e drops the
# slow browser/OCR end-to-end suite from the fast required gate.
- name: Test (fast unit suite)
run: |
mkdir -p runs
pytest -q --ignore=tests/e2e --basetemp=runs/ci \
--cov=openadapt_flow --cov-report=term-missing:skip-covered

- name: Upload run artifacts
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: runs-test
path: |
runs/**/REPORT.md
runs/**/BENCH.md
runs/**/report.json
runs/**/*.png
if-no-files-found: warn

# --- Post-merge / nightly: full Python matrix + macOS, FULL suite --------
# NOT a required check and NOT run on PRs (so merges never block on it). Runs
# the COMPLETE suite including the slow tests/e2e browser/OCR leg across
# Python 3.10-3.13 on Linux plus a macOS leg, on push to main and nightly.
test-matrix:
if: ${{ github.event_name != 'pull_request' }}
needs: lint
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ["3.10", "3.11", "3.12", "3.13"]
include:
# At least one non-Linux leg for the browser/OCR runtime.
- os: macos-latest
python-version: "3.12"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: ${{ matrix.python-version }}
cache: pip
cache-dependency-path: pyproject.toml

- name: Install
run: pip install -e .[dev]

- name: Resolve Playwright version
id: pw
run: echo "version=$(python -c 'import importlib.metadata as m; print(m.version(\"playwright\"))')" >> "$GITHUB_OUTPUT"

- name: Cache Playwright browsers
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ steps.pw.outputs.version }}

- name: Install Playwright browser
run: playwright install --with-deps chromium

- name: Test (full suite incl. e2e)
run: |
mkdir -p runs
pytest -q --basetemp=runs/ci \
--cov=openadapt_flow --cov-report=term-missing:skip-covered

- name: Upload run artifacts
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: runs
name: runs-${{ matrix.os }}-py${{ matrix.python-version }}
path: |
runs/**/REPORT.md
runs/**/BENCH.md
runs/**/report.json
runs/**/*.png
if-no-files-found: warn

# --- Windows: pure-Python / mock backend tests (no live VM, no browser) --
# Post-merge / nightly only (not a required check, not run on PRs).
windows-mock:
if: ${{ github.event_name != 'pull_request' }}
needs: lint
runs-on: windows-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml

- name: Install
run: pip install -e .[dev]

# The WindowsBackend + RDP backend suites mock the WAA/RDP servers with
# stdlib HTTP + fakes (no live VM, no Playwright browser), so they run
# anywhere. This proves the 4-method Backend protocol on Windows.
- name: Windows mock backend tests
run: pytest -q tests/test_windows_backend.py tests/test_rdp_backend.py

# --- Clean-wheel install: build, install in a fresh venv, import ---------
# Post-merge / nightly only (not a required check, not run on PRs).
wheel:
if: ${{ github.event_name != 'pull_request' }}
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml

- name: Build wheel
run: |
pip install build
python -m build --wheel

- name: Install the wheel in a clean venv and import
run: |
python -m venv /tmp/clean
/tmp/clean/bin/pip install dist/*.whl
# Import from a dir without the source tree; assert __version__ equals
# the installed distribution metadata (the drift bug this PR fixes --
# version-agnostic so it keeps holding across releases) and that the
# py.typed marker shipped inside the wheel.
cd /tmp && /tmp/clean/bin/python -c "import os, importlib.metadata as m, openadapt_flow; v = openadapt_flow.__version__; dist = m.version('openadapt-flow'); print('attr', v, 'dist', dist); assert v == dist, (v, dist); p = os.path.join(os.path.dirname(openadapt_flow.__file__), 'py.typed'); assert os.path.exists(p), 'py.typed missing from installed package'; print('py.typed present, import OK')"
Loading
Loading