diff --git a/docs/superpowers/plans/2026-04-10-orca-50-event-system-rebase.md b/docs/superpowers/plans/2026-04-10-orca-50-event-system-rebase.md new file mode 100644 index 0000000..421d8f3 --- /dev/null +++ b/docs/superpowers/plans/2026-04-10-orca-50-event-system-rebase.md @@ -0,0 +1,1447 @@ +# Orca 50 Event System Rebase Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Rebase Cthulhu's event, input, command, presentation, settings, and preferences manager slice onto official GNOME Orca 50.0.9 behavior while keeping Cthulhu TOML settings persistence. + +**Architecture:** Treat the official Orca 50.0.9 tarball as the source of truth and port the manager slice wholesale, translating package and branding names only. Cthulhu settings remain TOML-backed, but the persisted TOML data migrates to Orca 50 schema/key labels and is accessed through a `gsettings_registry` API facade. The old compositor-event path, old event queue, and old Glade preferences dialog are not final surfaces for the migrated slice. + +**Tech Stack:** Python 3.10+, PyGObject, AT-SPI 2.56+, GTK 3.24+, Meson, tomlkit, pytest, pytest-mock, official Orca 50.0.9 source from `https://download.gnome.org/sources/orca/50/orca-50.0.9.tar.xz`. + +--- + +## Source And Guardrails + +- Official source tree for this plan: `/tmp/orca-50.0.9-official` +- Official tarball SHA-256: `0c5dd0eb971e4694e3bd60ac9de76db587cc311ba802fda7380eaf1dce6a09cb` +- Do not edit `~/.local/.../cthulhu`. +- Do not preserve old event-manager behavior. +- Keep TOML as the only persisted Cthulhu settings store. +- Do not use dconf as a persistence backend. +- Preserve existing unrelated user edits. At plan-writing time the dirty files were `src/cthulhu/scripts/default.py`, `tests/test_default_script_clipboard_regressions.py`, and untracked `.codex`. + +## File Structure + +Create or port these upstream-derived modules under `src/cthulhu/`: + +- `ax_action.py` +- `ax_device_manager.py` +- `ax_utilities_action.py` +- `ax_utilities_component.py` +- `ax_utilities_object.py` +- `ax_utilities_selection.py` +- `ax_utilities_table.py` +- `ax_utilities_text.py` +- `braille_monitor.py` +- `braille_presenter.py` +- `bypass_mode_manager.py` +- `caret_navigator.py` +- `chat_presenter.py` +- `clipboard.py` +- `command_manager.py` +- `debugging_tools_manager.py` +- `document_presenter.py` +- `flat_review_finder.py` +- `gsettings_migrator.py` +- `gsettings_registry.py` +- `live_region_presenter.py` +- `preferences_grid_base.py` +- `presentation_manager.py` +- `profile_manager.py` +- `pronunciation_dictionary_manager.py` +- `say_all_presenter.py` +- `sound_presenter.py` +- `speech_manager.py` +- `speech_monitor.py` +- `speech_presenter.py` +- `spellcheck_presenter.py` +- `spiel.py` +- `ssml.py` +- `structural_navigator.py` +- `system_information_presenter.py` +- `systemd.py` +- `table_navigator.py` +- `text_attribute_manager.py` + +Modify these core files: + +- `meson.build` +- `pyproject.toml` +- `src/cthulhu/meson.build` +- `src/cthulhu/cthulhu.py` +- `src/cthulhu/cthulhu_modifier_manager.py` +- `src/cthulhu/event_manager.py` +- `src/cthulhu/guilabels.py` +- `src/cthulhu/input_event.py` +- `src/cthulhu/input_event_manager.py` +- `src/cthulhu/keybindings.py` +- `src/cthulhu/script.py` +- `src/cthulhu/script_manager.py` +- `src/cthulhu/scripts/default.py` +- toolkit/app scripts that still call removed camelCase script APIs after the base script port +- `src/cthulhu/settings.py` +- `src/cthulhu/settings_manager.py` +- `src/cthulhu/backends/toml_backend.py` +- `src/cthulhu/cthulhu_gui_prefs.py` +- `src/cthulhu/plugins/OCR/plugin.py` +- `src/cthulhu/plugins/AIAssistant/plugin.py` +- `src/cthulhu/plugin.py` +- `src/cthulhu/plugin_system_manager.py` +- `tests/conftest.py` + +Add or replace tests: + +- `tests/cthulhu_test_context.py` +- `tests/cthulhu_test_fixtures.py` +- `tests/test_gsettings_registry_toml.py` +- `tests/test_toml_settings_migration.py` +- `tests/test_ocr_preferences_grid.py` +- Cthulhu-ported versions of upstream tests for `event_manager`, `input_event_manager`, `command_manager`, `presentation_manager`, `script_manager`, `cthulhu_modifier_manager`, `preferences_grid_base`, and presenter modules. + +Remove or rewrite these old behavior tests when the Orca 50 event path lands: + +- `tests/test_event_manager_compositor_context_regressions.py` +- `tests/test_event_manager_relevance_gate_regressions.py` +- old event-manager assertions inside Steam/compositor-specific tests that only exist to enforce the discarded event path + +## Task 0: Isolate The Worktree And Verify Upstream + +**Files:** +- Read: `docs/superpowers/specs/2026-04-10-orca-50-event-system-rebase-design.md` +- No repo file edits in this task + +- [ ] **Step 1: Create a clean worktree** + +Run from `/home/sektor/projects/cthulhu`: + +```bash +git status --short +git worktree add -b orca50-event-rebase /tmp/cthulhu-orca50-event-rebase HEAD +cd /tmp/cthulhu-orca50-event-rebase +``` + +Expected: the new worktree is clean. The original checkout may still show unrelated user edits. + +- [ ] **Step 2: Verify the official Orca source** + +Run: + +```bash +test -f /tmp/orca-50.0.9.tar.xz +sha256sum /tmp/orca-50.0.9.tar.xz +test -d /tmp/orca-50.0.9-official/src/orca +``` + +Expected SHA line: + +```text +0c5dd0eb971e4694e3bd60ac9de76db587cc311ba802fda7380eaf1dce6a09cb /tmp/orca-50.0.9.tar.xz +``` + +If the tarball is missing, fetch and extract it: + +```bash +curl -fsSL https://download.gnome.org/sources/orca/50/orca-50.0.9.tar.xz -o /tmp/orca-50.0.9.tar.xz +python3 - <<'PY' +from pathlib import Path +import hashlib +path = Path("/tmp/orca-50.0.9.tar.xz") +digest = hashlib.sha256(path.read_bytes()).hexdigest() +expected = "0c5dd0eb971e4694e3bd60ac9de76db587cc311ba802fda7380eaf1dce6a09cb" +assert digest == expected, digest +PY +rm -rf /tmp/orca-50.0.9-official +mkdir -p /tmp/orca-50.0.9-official +tar -xf /tmp/orca-50.0.9.tar.xz -C /tmp/orca-50.0.9-official --strip-components=1 +``` + +- [ ] **Step 3: Record the dependency floor** + +Run: + +```bash +sed -n '1,45p' /tmp/orca-50.0.9-official/meson.build +sed -n '1,55p' meson.build +``` + +Expected finding: Orca 50.0.9 requires Python `>= 3.10`, `atspi-2 >= 2.56.0`, `atk-bridge-2.0 >= 2.56.0`, and GTK `3.24`. + +- [ ] **Step 4: Commit isolation baseline if needed** + +Run: + +```bash +git status --short +``` + +Expected: no changes in the new worktree. + +## Task 1: Update Dependency Floor And Test Harness + +**Files:** +- Modify: `meson.build` +- Modify: `pyproject.toml` +- Modify: `tests/conftest.py` +- Create: `tests/cthulhu_test_context.py` +- Create: `tests/cthulhu_test_fixtures.py` + +- [ ] **Step 1: Write the dependency-floor patch** + +Update `meson.build` to match the Orca 50 floor: + +```meson +python_minimum_version = '3.10' +dependency('atspi-2', version: '>= 2.56.0') +dependency('atk-bridge-2.0', version: '>= 2.56.0') +gtk_minor_version = '24' +``` + +Add `dasbus` to the hard Python module checks if it is not already hard-failed: + +```meson +dasbus_result = python.find_installation('python3', modules:['dasbus'], required: false) +if not dasbus_result.found() + error('dasbus is required for D-Bus remote controller interface') +endif +``` + +Update `pyproject.toml`: + +```toml +requires-python = ">=3.10" +dependencies = [ + "pygobject>=3.18", + "pluggy", + "tomlkit", + "dasbus", + "brlapi; extra == 'braille'", + "python-speechd; extra == 'speech'", + "piper-tts; extra == 'piper'", + "louis; extra == 'braille'" +] +``` + +- [ ] **Step 2: Port upstream pytest fixtures** + +Copy the upstream fixture files and translate package names: + +```bash +cp /tmp/orca-50.0.9-official/tests/unit_tests/orca_test_context.py tests/cthulhu_test_context.py +cp /tmp/orca-50.0.9-official/tests/unit_tests/orca_test_fixtures.py tests/cthulhu_test_fixtures.py +python3 - <<'PY' +from pathlib import Path +for path in [Path("tests/cthulhu_test_context.py"), Path("tests/cthulhu_test_fixtures.py")]: + text = path.read_text(encoding="utf-8") + replacements = [ + ("OrcaTestContext", "CthulhuTestContext"), + ("Orca tests", "Cthulhu tests"), + ("Orca screen reader", "Cthulhu screen reader"), + ("for Orca", "for Cthulhu"), + ("orca_test_context", "cthulhu_test_context"), + ("orca.", "cthulhu."), + ('"orca', '"cthulhu'), + ("'orca", "'cthulhu"), + (" orca", " cthulhu"), + ] + for old, new in replacements: + text = text.replace(old, new) + path.write_text(text, encoding="utf-8") +PY +``` + +Then edit `tests/cthulhu_test_fixtures.py` so it imports `CthulhuTestContext`: + +```python +from .cthulhu_test_context import CthulhuTestContext +``` + +And make the fixture return that class: + +```python +@pytest.fixture +def test_context(mocker: MockerFixture, monkeypatch) -> Generator[CthulhuTestContext, None, None]: + with CthulhuTestContext(mocker, monkeypatch) as context: + yield context +``` + +- [ ] **Step 3: Extend `tests/conftest.py` without removing generated-module setup** + +Append these imports and helpers to `tests/conftest.py`: + +```python +import os +import pytest + +from .cthulhu_test_fixtures import test_context # noqa: F401 + +os.environ["GSETTINGS_BACKEND"] = "memory" + + +def clean_all_cthulhu_modules() -> None: + modules_to_remove = [ + module_name + for module_name in sys.modules + if module_name.startswith("cthulhu.") + and module_name not in {"cthulhu.cthulhu_i18n", "cthulhu.cthulhu_platform"} + ] + for module_name in modules_to_remove: + sys.modules.pop(module_name, None) + + +def pytest_configure(config: pytest.Config) -> None: + config.addinivalue_line("markers", "unit: marks tests as unit tests") + + +def pytest_runtest_setup(item: pytest.Item) -> None: + clean_all_cthulhu_modules() + if str(SRC_ROOT) not in sys.path: + sys.path.insert(0, str(SRC_ROOT)) +``` + +Keep the existing `_load_generated_module()` logic at the top of the file. + +- [ ] **Step 4: Verify existing tests still load** + +Run: + +```bash +python3 -m pytest -q tests/test_input_event_regressions.py +``` + +Expected: the existing test either passes or fails only because the old input event implementation is about to be replaced. Import errors from `tests/conftest.py` must be fixed before continuing. + +- [ ] **Step 5: Commit** + +```bash +git add meson.build pyproject.toml tests/conftest.py tests/cthulhu_test_context.py tests/cthulhu_test_fixtures.py +git commit -m "Prepare test harness for Orca 50 rebase" +``` + +## Task 2: Define TOML Registry And Migration Tests First + +**Files:** +- Create: `tests/test_gsettings_registry_toml.py` +- Create: `tests/test_toml_settings_migration.py` + +- [ ] **Step 1: Add registry facade tests** + +Create `tests/test_gsettings_registry_toml.py`: + +```python +from pathlib import Path +from unittest import mock + +import pytest +from tomlkit import dumps, document + +from cthulhu import settings_manager + + +def _manager(tmp_path: Path): + manager = settings_manager.SettingsManager(mock.Mock()) + manager.activate(prefsDir=str(tmp_path)) + return manager + + +@pytest.mark.unit +def test_registry_defaults_to_default_profile(tmp_path): + _manager(tmp_path) + from cthulhu import gsettings_registry + + registry = gsettings_registry.get_registry() + registry.clear_runtime_values() + + assert registry.get_active_profile() == "default" + assert registry.get_active_app() is None + + +@pytest.mark.unit +def test_runtime_override_wins_over_toml(tmp_path): + _manager(tmp_path) + from cthulhu import gsettings_registry + + registry = gsettings_registry.get_registry() + registry.set_runtime_value("keybindings", "keyboard-layout", "laptop") + + assert registry.layered_lookup( + "keybindings", + "keyboard-layout", + "", + default="desktop", + ) == "laptop" + + +@pytest.mark.unit +def test_save_schema_writes_toml_not_dconf(tmp_path): + _manager(tmp_path) + from cthulhu import gsettings_registry + + registry = gsettings_registry.get_registry() + registry.save_schema( + "keybindings", + {"keyboard-layout": "laptop", "laptop-modifier-keys": ["Caps_Lock"]}, + "default", + "", + skip_defaults=False, + ) + + text = Path(tmp_path, "user-settings.toml").read_text(encoding="utf-8") + assert "keyboard-layout" in text + assert "laptop-modifier-keys" in text + assert "org.gnome.Orca" not in text + assert "dconf" not in text +``` + +- [ ] **Step 2: Add TOML migration tests** + +Create `tests/test_toml_settings_migration.py`: + +```python +from pathlib import Path + +import pytest +from tomlkit import dumps, document + + +def _write_legacy_settings(path: Path) -> None: + data = document() + data["general"] = { + "keyboardLayout": 1, + "cthulhuModifierKeys": ["Insert", "KP_Insert"], + "progressBarVerbosity": 1, + "progressBarUpdateInterval": 10, + "ocrLanguageCode": "eng", + "ocrScaleFactor": 3, + "ocrCopyToClipboard": True, + } + data["profiles"] = { + "default": { + "profile": ["Default", "default"], + "keybindings": {}, + "pronunciations": {}, + } + } + path.write_text(dumps(data), encoding="utf-8") + + +@pytest.mark.unit +def test_migrates_legacy_keys_to_schema_tables(tmp_path): + settings_path = Path(tmp_path, "user-settings.toml") + _write_legacy_settings(settings_path) + + from cthulhu import gsettings_registry + + registry = gsettings_registry.get_registry() + registry.migrate_all(str(tmp_path)) + + migrated = settings_path.read_text(encoding="utf-8") + assert 'format-version = 2' in migrated + assert "[profiles.default.keybindings]" in migrated + assert 'keyboard-layout = "desktop"' in migrated + assert 'desktop-modifier-keys = ["Insert", "KP_Insert"]' in migrated + assert "[profiles.default.ocr]" in migrated + assert 'language-code = "eng"' in migrated + assert "cthulhuModifierKeys" not in migrated + assert "progressBarUpdateInterval" not in migrated + + +@pytest.mark.unit +def test_migration_is_idempotent(tmp_path): + settings_path = Path(tmp_path, "user-settings.toml") + _write_legacy_settings(settings_path) + + from cthulhu import gsettings_registry + + registry = gsettings_registry.get_registry() + registry.migrate_all(str(tmp_path)) + first = settings_path.read_text(encoding="utf-8") + registry.migrate_all(str(tmp_path)) + second = settings_path.read_text(encoding="utf-8") + + assert second == first +``` + +- [ ] **Step 3: Run tests and verify expected failure** + +Run: + +```bash +python3 -m pytest -q tests/test_gsettings_registry_toml.py tests/test_toml_settings_migration.py +``` + +Expected: FAIL with an import error for `cthulhu.gsettings_registry`. + +- [ ] **Step 4: Commit the failing tests** + +```bash +git add tests/test_gsettings_registry_toml.py tests/test_toml_settings_migration.py +git commit -m "Add TOML registry migration tests" +``` + +## Task 3: Implement TOML-Backed Registry Facade + +**Files:** +- Create: `src/cthulhu/gsettings_migrator.py` +- Create: `src/cthulhu/gsettings_registry.py` +- Modify: `src/cthulhu/settings_manager.py` +- Modify: `src/cthulhu/backends/toml_backend.py` +- Modify: `src/cthulhu/settings.py` +- Modify: `src/cthulhu/meson.build` + +- [ ] **Step 1: Copy upstream migrator and keep only file-format helpers** + +Run: + +```bash +cp /tmp/orca-50.0.9-official/src/orca/gsettings_migrator.py src/cthulhu/gsettings_migrator.py +python3 - <<'PY' +from pathlib import Path +path = Path("src/cthulhu/gsettings_migrator.py") +text = path.read_text(encoding="utf-8") +text = text.replace("# Orca", "# Cthulhu", 1) +text = text.replace("Orca", "Cthulhu") +text = text.replace("orca", "cthulhu") +text = text.replace('"orcaModifierKeys"', '"cthulhuModifierKeys"') +path.write_text(text, encoding="utf-8") +PY +``` + +Then edit the module so these constants exist exactly: + +```python +LEGACY_KEY_ALIASES: dict[str, str] = { + "progressBarVerbosity": "progressBarSpeechVerbosity", + "progressBarUpdateInterval": "progressBarSpeechInterval", +} + +KEYBINDINGS_METADATA_KEYS: frozenset[str] = frozenset( + {"keyboardLayout", "cthulhuModifierKeys"} +) +``` + +- [ ] **Step 2: Implement the registry facade** + +Create `src/cthulhu/gsettings_registry.py` by starting from upstream and replacing dconf/Gio settings access with TOML access. Keep these public methods: + +```python +def get_registry() -> GSettingsRegistry: ... +class GSettingsRegistry: + def set_ignore_runtime(self, ignore: bool) -> None: ... + def set_active_app(self, app_name: str | None) -> None: ... + def set_active_profile(self, profile: str) -> None: ... + def get_active_app(self) -> str | None: ... + def get_active_profile(self) -> str: ... + def set_runtime_value(self, schema: str, key: str, value: object, voice_type: str | None = None) -> None: ... + def get_runtime_value(self, schema: str, key: str, voice_type: str | None = None) -> tuple[bool, object]: ... + def remove_runtime_value(self, schema: str, key: str, voice_type: str | None = None) -> None: ... + def clear_runtime_values(self) -> None: ... + def layered_lookup(self, schema: str, key: str, gtype: str, genum: str | None = None, voice_type: str | None = None, app_name: str | None = None, default: object = _NOT_SET) -> object | None: ... + def get_keybindings(self, profile: str = "", app_name: str = "") -> dict: ... + def get_pronunciations(self, profile: str = "", app_name: str = "") -> dict: ... + def save_schema(self, schema_name: str, settings: dict, profile: str, app_name: str = "", skip_defaults: bool = False) -> None: ... + def migrate_all(self, prefs_dir: str) -> bool: ... +``` + +The TOML v2 shape is: + +```toml +format-version = 2 + +[profiles.default.metadata] +display-name = "Default" +internal-name = "default" + +[profiles.default.keybindings] +keyboard-layout = "desktop" +desktop-modifier-keys = ["Insert", "KP_Insert"] +laptop-modifier-keys = ["Caps_Lock", "Shift_Lock"] + +[profiles.default.keybindings.entries] + +[profiles.default.pronunciations.entries] + +[profiles.default.ocr] +language-code = "eng" +scale-factor = 3 +grayscale-image = false +invert-image = false +black-white-image = false +black-white-threshold = 200 +color-calculation = false +color-calculation-max = 3 +copy-to-clipboard = false +``` + +App-specific settings use one TOML file per application under `app-settings/`, for example `app-settings/gedit.toml`, with schema-specific tables such as `[profiles.default.keybindings]` and `[profiles.default.ocr]`. + +- [ ] **Step 3: Add Cthulhu custom schema mappings** + +In `gsettings_registry.py`, include this migration alias table: + +```python +CTHULHU_LEGACY_SCHEMA_ALIASES: dict[str, tuple[str, str]] = { + "aiAssistantEnabled": ("ai-assistant", "enabled"), + "aiProvider": ("ai-assistant", "provider"), + "aiApiKeyFile": ("ai-assistant", "api-key-file"), + "aiOllamaModel": ("ai-assistant", "ollama-model"), + "aiOllamaEndpoint": ("ai-assistant", "ollama-endpoint"), + "aiConfirmationRequired": ("ai-assistant", "confirmation-required"), + "aiActionTimeout": ("ai-assistant", "action-timeout"), + "aiScreenshotQuality": ("ai-assistant", "screenshot-quality"), + "aiMaxContextLength": ("ai-assistant", "max-context-length"), + "ocrLanguageCode": ("ocr", "language-code"), + "ocrScaleFactor": ("ocr", "scale-factor"), + "ocrGrayscaleImg": ("ocr", "grayscale-image"), + "ocrInvertImg": ("ocr", "invert-image"), + "ocrBlackWhiteImg": ("ocr", "black-white-image"), + "ocrBlackWhiteImgValue": ("ocr", "black-white-threshold"), + "ocrColorCalculation": ("ocr", "color-calculation"), + "ocrColorCalculationMax": ("ocr", "color-calculation-max"), + "ocrCopyToClipboard": ("ocr", "copy-to-clipboard"), + "activePlugins": ("plugins", "active-plugins"), + "pluginSources": ("plugins", "plugin-sources"), +} +``` + +Add a special migration for old keyboard metadata: + +```python +def _migrate_legacy_modifier_keys(self, source: dict, target: dict) -> None: + layout = source.get("keyboardLayout", 1) + modifier_keys = source.get("cthulhuModifierKeys") + if layout == 2: + target["keyboard-layout"] = "laptop" + if modifier_keys is not None: + target["laptop-modifier-keys"] = list(modifier_keys) + else: + target["keyboard-layout"] = "desktop" + if modifier_keys is not None: + target["desktop-modifier-keys"] = list(modifier_keys) +``` + +- [ ] **Step 4: Connect registry activation to settings manager** + +At the end of `SettingsManager.activate()`, after `self.setProfile(self.profile)`, add: + +```python +from . import gsettings_registry + +gsettings_registry.get_registry().set_active_profile(self.profile or "default") +gsettings_registry.get_registry().migrate_all(self._prefsDir or "") +``` + +If import cycles occur, keep this import inside the method. + +- [ ] **Step 5: Update Meson install list** + +Add these to `cthulhu_python_sources` in `src/cthulhu/meson.build`: + +```meson +'gsettings_migrator.py', +'gsettings_registry.py', +``` + +- [ ] **Step 6: Run the registry tests** + +Run: + +```bash +python3 -m pytest -q tests/test_gsettings_registry_toml.py tests/test_toml_settings_migration.py +``` + +Expected: PASS. + +- [ ] **Step 7: Commit** + +```bash +git add src/cthulhu/gsettings_migrator.py src/cthulhu/gsettings_registry.py src/cthulhu/settings_manager.py src/cthulhu/backends/toml_backend.py src/cthulhu/settings.py src/cthulhu/meson.build +git commit -m "Add TOML-backed Orca 50 settings registry" +``` + +## Task 4: Port Upstream Helper Modules And Install Closure + +**Files:** +- Create: all upstream-derived helper modules listed in File Structure +- Modify: `src/cthulhu/meson.build` + +- [ ] **Step 1: Copy leaf helper modules** + +Run: + +```bash +for module in \ + ax_action.py ax_device_manager.py ax_utilities_action.py ax_utilities_component.py \ + ax_utilities_object.py ax_utilities_selection.py ax_utilities_table.py ax_utilities_text.py \ + braille_monitor.py bypass_mode_manager.py clipboard.py debugging_tools_manager.py \ + flat_review_finder.py preferences_grid_base.py systemd.py spiel.py ssml.py +do + cp "/tmp/orca-50.0.9-official/src/orca/${module}" "src/cthulhu/${module}" +done +``` + +- [ ] **Step 2: Translate package and branding references in copied modules** + +Run: + +```bash +python3 - <<'PY' +from pathlib import Path +for path in [Path(p) for p in """ +src/cthulhu/ax_action.py +src/cthulhu/ax_device_manager.py +src/cthulhu/ax_utilities_action.py +src/cthulhu/ax_utilities_component.py +src/cthulhu/ax_utilities_object.py +src/cthulhu/ax_utilities_selection.py +src/cthulhu/ax_utilities_table.py +src/cthulhu/ax_utilities_text.py +src/cthulhu/braille_monitor.py +src/cthulhu/bypass_mode_manager.py +src/cthulhu/clipboard.py +src/cthulhu/debugging_tools_manager.py +src/cthulhu/flat_review_finder.py +src/cthulhu/preferences_grid_base.py +src/cthulhu/systemd.py +src/cthulhu/spiel.py +src/cthulhu/ssml.py +""".split()]: + text = path.read_text(encoding="utf-8") + text = text.replace("Orca", "Cthulhu") + text = text.replace("ORCA", "CTHULHU") + text = text.replace("orca", "cthulhu") + text = text.replace("org.gnome.Cthulhu", "org.stormux.Cthulhu") + text = text.replace("org.gnome.cthulhu", "org.stormux.cthulhu") + path.write_text(text, encoding="utf-8") +PY +``` + +Then inspect the result: + +```bash +rg -n "orca|Orca|ORCA|org\\.gnome" src/cthulhu/ax_device_manager.py src/cthulhu/systemd.py src/cthulhu/preferences_grid_base.py +``` + +Expected: no package/branding references except comments that deliberately cite upstream origin. + +- [ ] **Step 3: Fix AT-SPI device name** + +In `src/cthulhu/ax_device_manager.py`, ensure device creation uses: + +```python +self._device = Atspi.Device.new_full("org.stormux.Cthulhu") +``` + +- [ ] **Step 4: Update Meson install list** + +Add all created modules to `src/cthulhu/meson.build` under `cthulhu_python_sources`. + +- [ ] **Step 5: Compile imported helpers** + +Run: + +```bash +python3 -m py_compile \ + src/cthulhu/ax_action.py \ + src/cthulhu/ax_device_manager.py \ + src/cthulhu/preferences_grid_base.py \ + src/cthulhu/systemd.py +``` + +Expected: no syntax errors. + +- [ ] **Step 6: Commit** + +```bash +git add src/cthulhu/meson.build src/cthulhu/ax_action.py src/cthulhu/ax_device_manager.py src/cthulhu/ax_utilities_action.py src/cthulhu/ax_utilities_component.py src/cthulhu/ax_utilities_object.py src/cthulhu/ax_utilities_selection.py src/cthulhu/ax_utilities_table.py src/cthulhu/ax_utilities_text.py src/cthulhu/braille_monitor.py src/cthulhu/bypass_mode_manager.py src/cthulhu/clipboard.py src/cthulhu/debugging_tools_manager.py src/cthulhu/flat_review_finder.py src/cthulhu/preferences_grid_base.py src/cthulhu/systemd.py src/cthulhu/spiel.py src/cthulhu/ssml.py +git commit -m "Port Orca 50 helper module closure" +``` + +## Task 5: Port Command, Input, Device, And Modifier Managers + +**Files:** +- Create: `src/cthulhu/command_manager.py` +- Modify: `src/cthulhu/cthulhu_modifier_manager.py` +- Modify: `src/cthulhu/input_event.py` +- Modify: `src/cthulhu/input_event_manager.py` +- Modify: `src/cthulhu/keybindings.py` +- Modify: `src/cthulhu/meson.build` +- Add tests ported from upstream manager tests + +- [ ] **Step 1: Copy manager files** + +Run: + +```bash +cp /tmp/orca-50.0.9-official/src/orca/command_manager.py src/cthulhu/command_manager.py +cp /tmp/orca-50.0.9-official/src/orca/input_event.py src/cthulhu/input_event.py +cp /tmp/orca-50.0.9-official/src/orca/input_event_manager.py src/cthulhu/input_event_manager.py +cp /tmp/orca-50.0.9-official/src/orca/orca_modifier_manager.py src/cthulhu/cthulhu_modifier_manager.py +``` + +- [ ] **Step 2: Translate imports and branding** + +Run: + +```bash +python3 - <<'PY' +from pathlib import Path +for path in [Path("src/cthulhu/command_manager.py"), Path("src/cthulhu/input_event.py"), Path("src/cthulhu/input_event_manager.py"), Path("src/cthulhu/cthulhu_modifier_manager.py")]: + text = path.read_text(encoding="utf-8") + replacements = [ + ("orca_modifier_manager", "cthulhu_modifier_manager"), + ("Orca Modifier", "Cthulhu Modifier"), + ("Orca modifier", "Cthulhu modifier"), + ("ORCA MODIFIER", "CTHULHU MODIFIER"), + ("ORCA", "CTHULHU"), + ("Orca", "Cthulhu"), + ("orca", "cthulhu"), + ("org.gnome.Cthulhu", "org.stormux.Cthulhu"), + ] + for old, new in replacements: + text = text.replace(old, new) + path.write_text(text, encoding="utf-8") +PY +``` + +Inspect for mistaken schema/member names: + +```bash +rg -n "cthulhuModifierKeys|orcaModifierKeys|org\\.gnome|orca_modifier|Orca|ORCA" src/cthulhu/command_manager.py src/cthulhu/input_event.py src/cthulhu/input_event_manager.py src/cthulhu/cthulhu_modifier_manager.py +``` + +Expected: user-facing/log strings say Cthulhu, module references use `cthulhu_modifier_manager`, and legacy `cthulhuModifierKeys` appears only in migration aliases. + +- [ ] **Step 3: Add manager tests** + +Copy upstream tests and translate names: + +```bash +cp /tmp/orca-50.0.9-official/tests/unit_tests/test_command_manager.py tests/test_command_manager.py +cp /tmp/orca-50.0.9-official/tests/unit_tests/test_input_event_manager.py tests/test_input_event_manager.py +cp /tmp/orca-50.0.9-official/tests/unit_tests/test_orca_modifier_manager.py tests/test_cthulhu_modifier_manager.py +python3 - <<'PY' +from pathlib import Path +for path in [Path("tests/test_command_manager.py"), Path("tests/test_input_event_manager.py"), Path("tests/test_cthulhu_modifier_manager.py")]: + text = path.read_text(encoding="utf-8") + text = text.replace(".orca_test_context", ".cthulhu_test_context") + text = text.replace("OrcaTestContext", "CthulhuTestContext") + text = text.replace("orca_modifier_manager", "cthulhu_modifier_manager") + text = text.replace("orca.", "cthulhu.") + text = text.replace("from orca", "from cthulhu") + text = text.replace("import orca", "import cthulhu") + text = text.replace("Orca", "Cthulhu") + text = text.replace("ORCA", "CTHULHU") + path.write_text(text, encoding="utf-8") +PY +``` + +- [ ] **Step 4: Update Meson** + +Ensure `src/cthulhu/meson.build` includes: + +```meson +'command_manager.py', +``` + +Existing entries for `input_event.py`, `input_event_manager.py`, and `cthulhu_modifier_manager.py` remain. + +- [ ] **Step 5: Run targeted tests** + +Run: + +```bash +python3 -m pytest -q tests/test_command_manager.py tests/test_input_event_manager.py tests/test_cthulhu_modifier_manager.py tests/test_gsettings_registry_toml.py +``` + +Expected: pass after fixing import names, descriptor enum IDs, and registry facade lookups. + +- [ ] **Step 6: Commit** + +```bash +git add src/cthulhu/command_manager.py src/cthulhu/input_event.py src/cthulhu/input_event_manager.py src/cthulhu/cthulhu_modifier_manager.py src/cthulhu/keybindings.py src/cthulhu/meson.build tests/test_command_manager.py tests/test_input_event_manager.py tests/test_cthulhu_modifier_manager.py +git commit -m "Port Orca 50 command and input managers" +``` + +## Task 6: Port Presentation Managers And Preferences Grids + +**Files:** +- Create presenter and navigator modules listed in File Structure +- Modify: `src/cthulhu/guilabels.py` +- Modify: `src/cthulhu/cthulhu_gui_prefs.py` +- Modify: `src/cthulhu/plugins/OCR/plugin.py` +- Modify: `src/cthulhu/plugins/AIAssistant/plugin.py` +- Modify: `src/cthulhu/plugin.py` +- Modify: `src/cthulhu/plugin_system_manager.py` +- Modify: `src/cthulhu/meson.build` +- Create: `tests/test_ocr_preferences_grid.py` + +- [ ] **Step 1: Copy presenter and navigator modules** + +Run: + +```bash +for module in \ + action_presenter.py braille_presenter.py caret_navigator.py chat_presenter.py \ + document_presenter.py flat_review_presenter.py live_region_presenter.py \ + notification_presenter.py object_navigator.py presentation_manager.py profile_manager.py \ + pronunciation_dictionary_manager.py say_all_presenter.py sound_presenter.py speech_manager.py \ + speech_monitor.py speech_presenter.py spellcheck_presenter.py structural_navigator.py \ + system_information_presenter.py table_navigator.py text_attribute_manager.py \ + typing_echo_presenter.py where_am_i_presenter.py guilabels.py +do + cp "/tmp/orca-50.0.9-official/src/orca/${module}" "src/cthulhu/${module}" +done +``` + +Translate package/branding references with the same explicit replacement style used in Task 5. + +- [ ] **Step 2: Port Orca 50 preferences dialog shell** + +Copy upstream preferences shell: + +```bash +cp /tmp/orca-50.0.9-official/src/orca/orca_gui_prefs.py src/cthulhu/cthulhu_gui_prefs.py +python3 - <<'PY' +from pathlib import Path +path = Path("src/cthulhu/cthulhu_gui_prefs.py") +text = path.read_text(encoding="utf-8") +for old, new in [ + ("orca_gui_prefs", "cthulhu_gui_prefs"), + ("orca_modifier_manager", "cthulhu_modifier_manager"), + ("from . import orca", "from . import cthulhu"), + (" orca.", " cthulhu."), + ("Orca", "Cthulhu"), + ("ORCA", "CTHULHU"), + ("orca", "cthulhu"), +]: + text = text.replace(old, new) +path.write_text(text, encoding="utf-8") +PY +``` + +Then wire Cthulhu-only grids into the new shell before dynamic plugin grids: + +```python +# Cthulhu-only pages. +ocr_grid = self._create_ocr_preferences_grid(title_change_callback=update_title) +self.stack.add_named(ocr_grid, "ocr") +self._add_navigation_row("ocr", ocr_grid.get_label().get_text()) +``` + +- [ ] **Step 3: Add OCR preferences grid** + +Add an `OCRPreferencesGrid` class in `src/cthulhu/plugins/OCR/plugin.py` or a dedicated `src/cthulhu/ocr_preferences_presenter.py`. Use the dedicated module if the plugin file grows past easy review size. + +The grid exposes these schema keys: + +```python +KEY_LANGUAGE_CODE = "language-code" +KEY_SCALE_FACTOR = "scale-factor" +KEY_GRAYSCALE_IMAGE = "grayscale-image" +KEY_INVERT_IMAGE = "invert-image" +KEY_BLACK_WHITE_IMAGE = "black-white-image" +KEY_BLACK_WHITE_THRESHOLD = "black-white-threshold" +KEY_COLOR_CALCULATION = "color-calculation" +KEY_COLOR_CALCULATION_MAX = "color-calculation-max" +KEY_COPY_TO_CLIPBOARD = "copy-to-clipboard" +``` + +Use `preferences_grid_base.AutoPreferencesGrid` controls: + +```python +preferences_grid_base.EnumPreferenceControl( + label=guilabels.OCR_LANGUAGE_CODE, + options=["eng"], + values=["eng"], + getter=self.get_language_code, + setter=self.set_language_code, + prefs_key=KEY_LANGUAGE_CODE, +) +preferences_grid_base.IntRangePreferenceControl( + label=guilabels.OCR_SCALE_FACTOR, + minimum=1, + maximum=5, + getter=self.get_scale_factor, + setter=self.set_scale_factor, + prefs_key=KEY_SCALE_FACTOR, +) +preferences_grid_base.BooleanPreferenceControl( + label=guilabels.OCR_COPY_TO_CLIPBOARD, + getter=self.get_copy_to_clipboard, + setter=self.set_copy_to_clipboard, + prefs_key=KEY_COPY_TO_CLIPBOARD, +) +``` + +Include controls for grayscale, invert, black-white, black-white threshold, color calculation, and color calculation max. + +- [ ] **Step 4: Add OCR GUI labels** + +Add labels to `src/cthulhu/guilabels.py`: + +```python +OCR = _("OCR") +OCR_LANGUAGE_CODE = _("OCR _language code:") +OCR_SCALE_FACTOR = _("Image _scale factor:") +OCR_GRAYSCALE_IMAGE = _("_Grayscale image") +OCR_INVERT_IMAGE = _("_Invert image") +OCR_BLACK_WHITE_IMAGE = _("_Black and white image") +OCR_BLACK_WHITE_THRESHOLD = _("Black and white _threshold:") +OCR_COLOR_CALCULATION = _("Use color _calculation") +OCR_COLOR_CALCULATION_MAX = _("Maximum _colors to report:") +OCR_COPY_TO_CLIPBOARD = _("_Copy recognized text to clipboard") +``` + +- [ ] **Step 5: Add AI and plugin preference access plan** + +Keep AI settings reachable in the new shell by adapting the existing plugin preference hook: + +```python +if hasattr(plugin_instance, "get_preferences_gui"): + page = plugin_instance.get_preferences_gui() +elif hasattr(plugin_instance, "getPreferencesGUI"): + page = plugin_instance.getPreferencesGUI() +``` + +Expose dynamic plugin pages under a `Plugins` navigation entry in the new stack. Preserve `active-plugins` and `plugin-sources` through the TOML `plugins` schema. + +- [ ] **Step 6: Add OCR preferences tests** + +Create `tests/test_ocr_preferences_grid.py`: + +```python +from unittest import mock + +import pytest + + +@pytest.mark.unit +def test_ocr_grid_saves_schema_keys(test_context): + test_context.setup_shared_dependencies(["cthulhu.preferences_grid_base"]) + from cthulhu.plugins.OCR import plugin + + grid = plugin.OCRPreferencesGrid() + grid.set_language_code("eng") + grid.set_scale_factor(4) + grid.set_copy_to_clipboard(True) + + result = grid.save_settings("default", "") + + assert result["language-code"] == "eng" + assert result["scale-factor"] == 4 + assert result["copy-to-clipboard"] is True +``` + +- [ ] **Step 7: Port upstream presenter tests** + +Copy and translate these upstream tests: + +```bash +for test in \ + test_preferences_grid_base.py test_presentation_manager.py test_profile_manager.py \ + test_braille_presenter.py test_speech_presenter.py test_sound_presenter.py \ + test_typing_echo_presenter.py test_say_all_presenter.py test_pronunciation_dictionary_manager.py \ + test_document_presenter.py test_caret_navigator.py test_structural_navigator.py \ + test_table_navigator.py test_text_attribute_manager.py test_spellcheck_presenter.py \ + test_live_region_presenter.py +do + cp "/tmp/orca-50.0.9-official/tests/unit_tests/${test}" "tests/${test}" +done +``` + +Translate package names with the same test translation script from Task 5. + +- [ ] **Step 8: Run preferences and presenter tests** + +Run: + +```bash +python3 -m pytest -q \ + tests/test_preferences_grid_base.py \ + tests/test_presentation_manager.py \ + tests/test_braille_presenter.py \ + tests/test_speech_presenter.py \ + tests/test_sound_presenter.py \ + tests/test_ocr_preferences_grid.py +``` + +Expected: PASS. + +- [ ] **Step 9: Commit** + +```bash +git add src/cthulhu src/cthulhu/plugins/OCR/plugin.py src/cthulhu/plugins/AIAssistant/plugin.py tests/test_preferences_grid_base.py tests/test_presentation_manager.py tests/test_braille_presenter.py tests/test_speech_presenter.py tests/test_sound_presenter.py tests/test_ocr_preferences_grid.py +git commit -m "Port Orca 50 presentation and preferences layer" +``` + +## Task 7: Port Script And Script Manager APIs + +**Files:** +- Modify: `src/cthulhu/script.py` +- Modify: `src/cthulhu/script_manager.py` +- Modify: `src/cthulhu/scripts/default.py` +- Modify: toolkit/app scripts that reference removed methods +- Add: `tests/test_script_manager.py` + +- [ ] **Step 1: Copy upstream base script modules** + +Run: + +```bash +cp /tmp/orca-50.0.9-official/src/orca/script.py src/cthulhu/script.py +cp /tmp/orca-50.0.9-official/src/orca/script_manager.py src/cthulhu/script_manager.py +cp /tmp/orca-50.0.9-official/src/orca/scripts/default.py src/cthulhu/scripts/default.py +``` + +Translate package and branding references. Keep Cthulhu plugin activation calls only after the new script API compiles. + +- [ ] **Step 2: Ensure final script API names are snake_case** + +Run: + +```bash +rg -n "getListeners|registerEventListeners|deregisterEventListeners|processObjectEvent|presentIfInactive|eventCache|inputEventHandlers|keyBindings|brailleBindings" src/cthulhu +``` + +Expected after this task: no final callers of those names remain in the migrated manager slice. + +The replacement names are: + +```text +getListeners -> get_listeners +registerEventListeners -> register_event_listeners +deregisterEventListeners -> deregister_event_listeners +processObjectEvent -> direct listener methods registered from get_listeners +presentIfInactive -> present_if_inactive +eventCache -> event_cache +inputEventHandlers/keyBindings/brailleBindings -> command_manager-owned commands +``` + +- [ ] **Step 3: Port scripts that still import old names** + +For each hit from the `rg` command, either port the script to the Orca 50 method name or remove the override if upstream Orca 50 no longer has it. Use upstream app/toolkit scripts where names match: + +```bash +find /tmp/orca-50.0.9-official/src/orca/scripts -name 'script.py' -print +``` + +Do not keep a compatibility alias in `Script` after all callers are ported. + +- [ ] **Step 4: Port upstream script manager tests** + +Run: + +```bash +cp /tmp/orca-50.0.9-official/tests/unit_tests/test_script_manager.py tests/test_script_manager.py +python3 - <<'PY' +from pathlib import Path +path = Path("tests/test_script_manager.py") +text = path.read_text(encoding="utf-8") +text = text.replace(".orca_test_context", ".cthulhu_test_context") +text = text.replace("OrcaTestContext", "CthulhuTestContext") +text = text.replace("orca.", "cthulhu.") +text = text.replace("from orca", "from cthulhu") +text = text.replace("import orca", "import cthulhu") +text = text.replace("Orca", "Cthulhu") +text = text.replace("ORCA", "CTHULHU") +path.write_text(text, encoding="utf-8") +PY +``` + +- [ ] **Step 5: Run script tests** + +Run: + +```bash +python3 -m pytest -q tests/test_script_manager.py tests/test_command_manager.py tests/test_gsettings_registry_toml.py +python3 -m py_compile src/cthulhu/script.py src/cthulhu/script_manager.py src/cthulhu/scripts/default.py +``` + +Expected: PASS and no syntax errors. + +- [ ] **Step 6: Commit** + +```bash +git add src/cthulhu/script.py src/cthulhu/script_manager.py src/cthulhu/scripts tests/test_script_manager.py +git commit -m "Port Orca 50 script manager API" +``` + +## Task 8: Replace Event Manager Last + +**Files:** +- Modify: `src/cthulhu/event_manager.py` +- Modify: `src/cthulhu/cthulhu.py` +- Add: `tests/test_event_manager.py` +- Remove or rewrite obsolete event-manager regression tests + +- [ ] **Step 1: Copy upstream event manager** + +Run: + +```bash +cp /tmp/orca-50.0.9-official/src/orca/event_manager.py src/cthulhu/event_manager.py +``` + +Translate package and branding references. Do not reintroduce `set_compositor_state_adapter`, compositor relevance gates, Steam churn filters, self-hosted focus prioritization, or the old `queue.Queue` path. + +- [ ] **Step 2: Remove old compositor adapter wiring from the event path** + +In `src/cthulhu/cthulhu.py`, remove the event manager adapter injection: + +```python +self.compositorStateAdapter: CompositorStateAdapter = compositor_state_adapter.CompositorStateAdapter() +self.eventManager.set_compositor_state_adapter(self.compositorStateAdapter) +``` + +If other non-event code still needs compositor state, keep the adapter as a separate `Cthulhu` member. It must not be used by `event_manager.py`. + +- [ ] **Step 3: Update lifecycle to Orca 50 service activation** + +Port `loadUserSettings`, startup activation, and shutdown to use: + +```python +ax_device_manager.get_manager().activate() +event_manager.get_manager().activate() +script_manager.get_manager().activate() +presentation_manager.get_manager().start_presenters() +command_manager.get_manager().load_keyboard_layout() +``` + +Use Cthulhu plugin activation after settings load, preserving plugin functionality without changing event semantics. + +- [ ] **Step 4: Port upstream event manager tests** + +Run: + +```bash +cp /tmp/orca-50.0.9-official/tests/unit_tests/test_event_manager.py tests/test_event_manager.py +python3 - <<'PY' +from pathlib import Path +path = Path("tests/test_event_manager.py") +text = path.read_text(encoding="utf-8") +text = text.replace(".orca_test_context", ".cthulhu_test_context") +text = text.replace("OrcaTestContext", "CthulhuTestContext") +text = text.replace("orca_modifier_manager", "cthulhu_modifier_manager") +text = text.replace("orca.", "cthulhu.") +text = text.replace("from orca", "from cthulhu") +text = text.replace("import orca", "import cthulhu") +text = text.replace("Orca", "Cthulhu") +text = text.replace("ORCA", "CTHULHU") +path.write_text(text, encoding="utf-8") +PY +``` + +- [ ] **Step 5: Remove obsolete old-event tests** + +Run: + +```bash +git rm tests/test_event_manager_compositor_context_regressions.py +git rm tests/test_event_manager_relevance_gate_regressions.py +``` + +Then scan for discarded event-path expectations: + +```bash +rg -n "compositor|relevance|Steam|self-hosted|prioritized|asyncMode|set_compositor_state_adapter|_prioritizedEvent" tests src/cthulhu/event_manager.py src/cthulhu/cthulhu.py +``` + +Expected: no event-manager tests enforce discarded behavior. Compositor source files may still exist if other non-event features use them. + +- [ ] **Step 6: Run event manager tests** + +Run: + +```bash +python3 -m pytest -q tests/test_event_manager.py tests/test_input_event_manager.py tests/test_script_manager.py +python3 -m py_compile src/cthulhu/event_manager.py src/cthulhu/cthulhu.py +``` + +Expected: PASS and no syntax errors. + +- [ ] **Step 7: Commit** + +```bash +git add src/cthulhu/event_manager.py src/cthulhu/cthulhu.py tests/test_event_manager.py +git add -u tests/test_event_manager_compositor_context_regressions.py tests/test_event_manager_relevance_gate_regressions.py +git commit -m "Replace event manager with Orca 50 model" +``` + +## Task 9: Import Closure Sweep And Meson Verification + +**Files:** +- Modify: `src/cthulhu/meson.build` +- Modify any newly missing module imports found by compile/import tests + +- [ ] **Step 1: Compare installed source lists** + +Run: + +```bash +python3 - <<'PY' +from pathlib import Path +import ast + +def meson_files(path, variable): + text = Path(path).read_text(encoding="utf-8") + tree = ast.literal_eval("[" + text.split("files([", 1)[1].split("])", 1)[0] + "]") + return set(tree) + +cthulhu = meson_files("src/cthulhu/meson.build", "cthulhu_python_sources") +orca_text = Path("/tmp/orca-50.0.9-official/src/orca/meson.build").read_text(encoding="utf-8") +lib_block = orca_text.split("lib_sources = [", 1)[1].split("]", 1)[0] +orca = {line.strip().strip("',") for line in lib_block.splitlines() if line.strip().startswith("'")} +expected_cthulhu_only = { + "cthulhu.py", + "cthulhu_gtkbuilder.py", + "cthulhu_gui_find.py", + "cthulhu_gui_navlist.py", + "cthulhu_gui_profile.py", + "settings_manager.py", + "signal_manager.py", + "plugin.py", + "plugin_system_manager.py", + "resource_manager.py", +} +missing = sorted(m for m in orca if m not in cthulhu and m.replace("orca", "cthulhu") not in cthulhu) +print("missing_from_cthulhu:", missing) +assert not [m for m in missing if m not in {"orca.py", "orca_gui_navlist.py", "orca_gui_prefs.py", "orca_modifier_manager.py"}] +PY +``` + +Expected: no missing manager/presenter/helper modules except renamed Orca entrypoints. + +- [ ] **Step 2: Compile the package** + +Run: + +```bash +python3 -m compileall -q src/cthulhu +``` + +Expected: no syntax errors. + +- [ ] **Step 3: Meson setup and compile** + +Run: + +```bash +meson setup _build --prefix="$HOME/.local" --reconfigure +meson compile -C _build +``` + +Expected: Meson configures with Python 3.10+, AT-SPI 2.56+, GTK 3.24+, and compiles. + +- [ ] **Step 4: Commit** + +```bash +git add src/cthulhu/meson.build meson.build pyproject.toml +git status --short +git commit -m "Complete Orca 50 import closure" +``` + +Only commit if `git status --short` shows intended files. + +## Task 10: Full Test And Local Install Verification + +**Files:** +- No planned source edits unless verification exposes missing porting work + +- [ ] **Step 1: Run targeted migrated suites** + +Run: + +```bash +python3 -m pytest -q \ + tests/test_gsettings_registry_toml.py \ + tests/test_toml_settings_migration.py \ + tests/test_preferences_grid_base.py \ + tests/test_command_manager.py \ + tests/test_input_event_manager.py \ + tests/test_cthulhu_modifier_manager.py \ + tests/test_presentation_manager.py \ + tests/test_script_manager.py \ + tests/test_event_manager.py \ + tests/test_ocr_preferences_grid.py +``` + +Expected: PASS. + +- [ ] **Step 2: Run the full test suite** + +Run: + +```bash +HOME=/tmp/cthulhu-test-home python3 -m pytest -q tests +``` + +Expected: PASS or failures documented as unrelated pre-existing tests. Event-path failures must be fixed in this branch. + +- [ ] **Step 3: Build and apply local install** + +Run: + +```bash +./build-local.sh +``` + +Expected: local build and install under `$HOME/.local` completes. + +- [ ] **Step 4: Run local sanity checks** + +Run: + +```bash +./test-local.sh +``` + +Expected: version/help checks pass. D-Bus service check may report the existing development warning only if that was already acceptable in this repo. + +- [ ] **Step 5: Smoke import installed package** + +Run: + +```bash +python3 - <<'PY' +import cthulhu.event_manager +import cthulhu.input_event_manager +import cthulhu.command_manager +import cthulhu.presentation_manager +import cthulhu.gsettings_registry +print("cthulhu manager imports ok") +PY +``` + +Expected: + +```text +cthulhu manager imports ok +``` + +- [ ] **Step 6: Record final verification status** + +Run: + +```bash +git status --short +``` + +Expected: no unstaged or untracked files except files intentionally left for a task-specific follow-up. If verification required fixes, return to the task that owns the changed file, apply the fix there, rerun that task's tests, and make the task-specific commit before repeating Task 10. + +## Self-Review Checklist + +- Spec coverage: + - Wholesale manager slice covered by Tasks 4 through 9. + - TOML-backed registry and migration covered by Tasks 2 and 3. + - Preferences grid and GUI labels covered by Task 6. + - OCR settings covered by Task 6. + - Event manager replacement covered by Task 8. + - Obsolete compositor event tests covered by Task 8. + - Meson install integration covered by Tasks 3, 4, 5, 6, and 9. + - Verification and local install covered by Task 10. +- Type consistency: + - Registry API uses Orca 50 names: `layered_lookup`, `set_runtime_value`, `clear_runtime_values`, `save_schema`. + - Script API uses Orca 50 snake_case names. + - TOML v2 schema keys use kebab-case labels such as `keyboard-layout` and `copy-to-clipboard`. +- Residual risk: + - This is a large rebase. Execute in a dedicated worktree and commit after each task.