Skip to content

batch: teach scope, run authorizations, redaction, merge queue, main dedupe, Desktop program map, tray fold - #1421

Merged
abrichr merged 141 commits into
mainfrom
batch/2026-10-20
Oct 20, 2026
Merged

abrichr merged 141 commits into
mainfrom
batch/2026-10-20

Conversation

@abrichr

@abrichr abrichr commented Oct 20, 2026

Copy link
Copy Markdown
Member

One CI run instead of eight. Every PR folded in here was reviewed separately; their own threads have the reviews.

Conflicts resolved here:

Local checks: 244 sealing, authorization, redaction, hint and deferred-report tests; 35 workflow tests; ruff; gitleaks on the new commits; prospect-name check. Merging this closes the eight PRs.

🤖 Generated with Claude Code

abrichr and others added 30 commits January 17, 2026 00:23
This commit implements the openadapt-tray system tray application for OpenAdapt,
based on the design document at docs/design/openadapt-tray.md.

Features:
- Cross-platform system tray icon using pystray (lightweight alternative to Qt)
- Start/Stop recording menu items that delegate to openadapt CLI
- Global hotkeys (Ctrl+Shift+R for recording, Ctrl+Shift+D for dashboard)
- Triple-ctrl stop sequence for legacy compatibility
- State machine for application state management
- Platform-specific handlers for macOS, Windows, and Linux
- Native dialogs using AppleScript (macOS), ctypes (Windows), and zenity/kdialog (Linux)
- Notification support across all platforms
- Configuration persistence in platform-appropriate locations
- IPC client for communication with other OpenAdapt processes
- Placeholder icons (colored circles) for different states

Package structure:
- src/openadapt_tray/ - Main package code
  - app.py - Main TrayApplication class
  - menu.py - Menu construction
  - icons.py - Icon management
  - notifications.py - Cross-platform notifications
  - shortcuts.py - Global hotkey handling
  - config.py - Configuration management
  - ipc.py - Inter-process communication
  - state.py - Application state machine
  - platform/ - Platform-specific handlers
- assets/icons/ - State icons (idle, recording, training, error)
- tests/ - Test suite (78 passing tests)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add standard badges for license and Python version. PyPI badges are
commented out until the package is published.

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: add README badges for license and Python version

Add standard badges for license and Python version. PyPI badges are
commented out until the package is published.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: implement desktop-notifier notification system

Implement modern, native notification system using desktop-notifier library
based on comprehensive review in task a95f1e8. Replaces legacy AppleScript/
PowerShell implementations with cross-platform solution.

Key features:
- Native notifications on macOS (Notification Center), Windows (WinRT),
  and Linux (DBus)
- Click callbacks for notification interactions
- Urgency levels (low, normal, critical)
- Action buttons (platform-dependent)
- Reply fields (macOS only)
- Custom icons and duration control
- Async and sync APIs
- Graceful fallback to platform-specific implementations
- Full backward compatibility with existing API

Changes:
- Completely rewrote src/openadapt_tray/notifications.py (390 lines)
  - Added desktop-notifier backend with event loop management
  - Maintained fallback to AppleScript/PowerShell/notify-send
  - Implemented sync wrapper around async desktop-notifier API
  - Added resource cleanup on app shutdown

- Updated src/openadapt_tray/app.py
  - Added notifications.cleanup() call in quit() method

- Updated pyproject.toml
  - Added desktop-notifier>=6.2.0 dependency

- Updated README.md
  - Added native notifications to features list
  - Added desktop-notifier to dependencies

Documentation:
- Added NOTIFICATIONS.md: Comprehensive notification system guide (9.4 KB)
  - Usage examples for all features
  - Architecture and backend detection
  - Platform-specific notes and troubleshooting
  - Complete API reference
  - Migration guide

- Added examples/notification_examples.py: 10 comprehensive examples
  - Basic notifications
  - Callbacks and urgency levels
  - Action buttons and reply fields
  - Conditional and state-based notifications

- Added test scripts:
  - test_notification_simple.py: Quick non-interactive test
  - test_notification.py: Interactive test with user interaction
  - examples/README.md: Example documentation

- Added IMPLEMENTATION_SUMMARY.md: Complete implementation documentation

Testing:
- Verified backend detection and fallback mechanisms
- Tested basic, critical, and callback notifications
- Confirmed backward compatibility with existing API
- Manual testing available via test_notification_simple.py

Benefits:
- Modern native notification experience
- Better platform integration
- Rich interactive features
- Clean, maintainable API
- No breaking changes

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: detect app bundle on macOS and fall back to AppleScript

Fixes critical bug where desktop-notifier fails with "'NoneType'
object is not iterable" when not running from an app bundle on macOS.

Changes:
- Add app bundle detection in _detect_backend() method
- Check both APP_BUNDLE env var and bundle path structure
- Automatically fall back to AppleScript if not in app bundle
- Add os import for environment variable checking
- Fix README.md desktop-notifier version (6.0.0 -> 6.2.0)

The AppleScript fallback works perfectly for non-bundled execution
(e.g., development, CLI usage) while desktop-notifier can still be
used when running from a proper macOS app bundle.

Tested: All notifications now work correctly via AppleScript fallback.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: Improve notification backend detection and make IPC optional

Fixes two critical startup issues:

1. Notification Center app bundle requirement:
   - Try to initialize desktop-notifier even outside app bundle
   - Gracefully fall back to AppleScript if initialization fails
   - Provides better error messages for debugging

2. IPC connection blocking startup:
   - Wrap IPC connection in try-except to prevent startup failures
   - Add informative messages for connection status
   - App now runs in standalone mode if IPC server unavailable

These changes make the tray app more resilient and provide better
user experience when running outside of a full app bundle or when
the OpenAdapt backend is not running.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: Add automated screenshot capture workflow

Simplifies screenshot capture and organization workflow with two new files:

1. rename_screenshots.py:
   - Automatically finds recent screenshots from Desktop
   - Interactive renaming with confirmation
   - Moves to docs/screenshots/ with standardized names
   - Backs up existing files to prevent data loss
   - Supports both "Screen Shot" and "Screenshot" formats (macOS variations)
   - Shows timestamps to help verify correct screenshot order

2. SCREENSHOT_SIMPLE.md:
   - Step-by-step guide for simplified screenshot workflow
   - Takes 10-15 minutes total
   - Just take screenshots with Cmd+Shift+4, run script, commit
   - No manual renaming needed
   - Includes troubleshooting section
   - Covers required and optional screenshots

User workflow:
  1. Take 5-8 screenshots with Cmd+Shift+4
  2. Run: python rename_screenshots.py
  3. Commit: git add docs/screenshots && git commit

This makes documentation screenshots much easier to capture and
organize, reducing manual work from 30+ minutes to 10-15 minutes.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
- publish.yml: Triggered on tags, publishes to PyPI
- release.yml: Auto-bumps version on PR merge, creates tags

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replaces manual commit parsing with python-semantic-release:
- Automatic version bumping based on conventional commits
- feat: -> minor, fix:/perf: -> patch
- Creates GitHub releases automatically
- Publishes to PyPI on release

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The python-semantic-release action runs in a Docker container where uv
is not available. Let the workflow handle building instead.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace `branch = "main"` (v7/v8 key) with `[tool.semantic_release.branches.main]`
table (v9 key). The old key is silently ignored by v9, causing releases to never
trigger on the main branch.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Reposition the Experimental tray around record, compile, replay, and governed hosted repair, with protected-main and pre-1.0 release safeguards.
Replace the undefined release lock variable with an offline exact-root synchronizer and preserve v0.1.0 as the failed-attempt audit record.
Supersedes the original whole-file rewrite: main's README (from #5)
already covers the loop status/launcher role, so this PR now only fixes
the claims that went stale when the hosted lifecycle merged and 0.1.1
shipped to PyPI:

- hosted behavior is on main and released, not 'feat/hosted-rewire branch code'
- version is 0.1.1 (pre-alpha classifier), not 0.0.1
- add the PyPI badge and a real pip install quickstart
- keep the honest boundary: no released openadapt-desktop build provides
  the companion IPC service yet

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Match the AGENTS.md / org-profile / LIMITS.md canonical one-liner
('retained evidence'); the tray README said 'recorded evidence'.


Claude-Session: https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ot) (#9)

Adds CodeQL SAST, gitleaks secret scanning, dependency review, Dependabot, and a vulnerability-disclosure policy as part of SOC 2 readiness. Additive and non-breaking.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Replace the plain solid colored circle with the OpenAdapt brand mark
(the chat/robot face from the openadapt.ai favicon), color-stylized per
recording-lifecycle state so the state stays readable at a glance while
the brand is finally shown in the menu bar / system tray.

- Add the vector mark (assets/mark.svg) and a high-res transparent raster
  master (assets/mark-master.png), plus a 512px copy packaged inside the
  wheel (src/openadapt_tray/assets/mark-master.png) so the installed app
  can render the mark. The wheel previously shipped no assets, so it
  always fell back to a generated circle.
- IconManager now tints the mark's alpha silhouette per state
  (idle=brand blue, recording=red, starting/stopping=amber,
  compiling=purple, error=red). One tint implementation is shared by the
  runtime and the generator, so committed PNGs and runtime icons never
  diverge. The needs-attention badge is preserved.
- Give every TrayState its own icon file. This fixes a latent bug where
  COMPILING referenced a nonexistent compiling.png (fell back to a
  circle) and starting/stopping showed red instead of amber.
- Rewrite scripts/generate_icons.py to re-render the master from the SVG
  (rsvg-convert/cairosvg/inkscape) and tint per-state 1x + @2x PNGs and a
  multi-size logo.ico. Remove the stale training.png icons.

Icons verified valid at menu-bar sizes (22px + 44px, light and dark);
137 tests pass, ruff clean.


Claude-Session: https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
#10)

Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4.9.0 to 5.0.0.
- [Release notes](https://github.com/actions/dependency-review-action/releases)
- [Commits](actions/dependency-review-action@2031cfc...a1d282b)

---
updated-dependencies:
- dependency-name: actions/dependency-review-action
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [python-semantic-release/publish-action](https://github.com/python-semantic-release/publish-action) from 9.15.2 to 10.6.1.
- [Release notes](https://github.com/python-semantic-release/publish-action/releases)
- [Changelog](https://github.com/python-semantic-release/publish-action/blob/main/releaserc.toml)
- [Commits](python-semantic-release/publish-action@b9c41d4...5a5718c)

---
updated-dependencies:
- dependency-name: python-semantic-release/publish-action
  dependency-version: 10.6.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@9c091bb...3d3c42e)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6.3.0 to 7.0.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](actions/setup-python@ece7cb0...5fda3b9)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [python-semantic-release/python-semantic-release](https://github.com/python-semantic-release/python-semantic-release) from 9.15.2 to 10.6.1.
- [Release notes](https://github.com/python-semantic-release/python-semantic-release/releases)
- [Changelog](https://github.com/python-semantic-release/python-semantic-release/blob/master/CHANGELOG.rst)
- [Commits](python-semantic-release/python-semantic-release@7b3f716...39dd205)

---
updated-dependencies:
- dependency-name: python-semantic-release/python-semantic-release
  dependency-version: 10.6.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ntract (#18)

Rewrite the README around the shared OpenAdapt positioning (governed
demonstration compiler; substrates first-class with an honest maturity ladder;
local-first; MIT open-core) and describe what the tray actually does: start/stop
recording, recent captures, needs-attention count and routing, open desktop app
or cloud dashboard, sync channel, login, and settings.

Document the per-state OpenAdapt-mark icon (one mark tinted per recording/compile
lifecycle state) and how the tray fits with openadapt-desktop and the cloud.

Update the stale cross-repo claim: the openadapt-desktop main branch now serves
the exact discovery-socket and command contract this tray expects, so the README
no longer says no build implements it. It still states plainly that the two
surfaces have not been validated together end to end and that no signed desktop
build ships the server yet. Update the metadata test to assert that honest
boundary instead of the retired string. Cross-link docs.openadapt.ai. No em dashes.


Claude-Session: https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* chore(deps-dev): bump uv from 0.11.26 to 0.11.29

Bumps [uv](https://github.com/astral-sh/uv) from 0.11.26 to 0.11.29.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](astral-sh/uv@0.11.26...0.11.29)

---
updated-dependencies:
- dependency-name: uv
  dependency-version: 0.11.29
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps-dev): synchronize uv 0.11.29 lock

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: abrichr <richard.abrich@mldsai.com>
…er (#26)

Bumping only one of init/analyze leaves the pair at mismatched versions, which
CodeQL rejects with a configuration error. Bump both in one commit and group
future codeql-action updates so Dependabot cannot split them again.


Claude-Session: https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 8.3.2 to 9.0.0.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](astral-sh/setup-uv@11f9893...c771a70)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: 9.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
)

Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.14.0 to 1.14.1.
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](pypa/gh-action-pypi-publish@cef2210...ba38be9)

---
updated-dependencies:
- dependency-name: pypa/gh-action-pypi-publish
  dependency-version: 1.14.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
abrichr and others added 26 commits October 20, 2026 01:40
# Conflicts:
#	.github/workflows/packages.yml
#	CLAUDE.md
#	llms-full.txt
HIGH
- The statement names its signer (signer_fingerprint); a signature by any
  other key is auth_signer_mismatch. The stored GitHub copy no longer
  vouches for identity: identity_verified only with --live (github.com,
  HTTPS, no redirects, no proxy) or caller-pinned --allowed-signers;
  otherwise 'identity not verified (self-asserted github:<u>)'. The
  operator profile links only verified identities; profile gets --live.
MEDIUM
- before_run only when auth log time < started_at <= Seal log time, both
  anchored; otherwise after_run or not_anchored.
- authorize sign signs a private 0600 copy of exactly the shown bytes,
  passes -- to ssh-keygen, and writes the .sig only if the file is
  unchanged.
- The profile counts a Seal or Rekor entry once (excluded.duplicate).
LOW
- Summary strips control characters and ANSI; an unbound QUALIFICATION.md
  is marked 'not covered by its digest'.
- sign exits non-zero when the new signature doesn't verify.
- The authorization's anchor must be by a Seal key in scope.
- scope.reusable (default false): one run per item; verify over several
  runs and the profile reject reuse (auth_item_reused).
- report.json is read with the duplicate-key-rejecting loader everywhere.
- Items and scope_item are short labels (a lower-case prefix and a number).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…els, reuse scope

- The operator profile counts a report once: the same Seal, Rekor entry,
  or report re-sealed with a new issued_at and anchored again (by
  subject_sha256), keeping the Seal the log recorded first.
- Items and scope_item are one or two lower-case letters and up to three
  digits (r3); mrn12345-style labels are refused. A Seal's item must be
  one the statement lists.
- Docs: reuse is seen only among Seals verified together.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
flow: compile named sections for every program; the program map and Desktop fold them
Conflict: packages/desktop/src/lib/engine.ts, where this branch's
EDIT_PROGRAM_SECTIONS (from #1405) and main's GET_CHECK_SETUP /
ANSWER_CHECK_SETUP were added at the same place; both are kept.
llms-full.txt regenerated with scripts/build_llms_full.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@abrichr
abrichr merged commit e931903 into main Oct 20, 2026
52 checks passed
@abrichr
abrichr deleted the batch/2026-10-20 branch October 20, 2026 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant