Skip to content

Latest commit

 

History

History
252 lines (194 loc) · 35.3 KB

File metadata and controls

252 lines (194 loc) · 35.3 KB

Research — OpenTasker

Date: 2026-08-02 — replaces all prior research.

Tree state at time of writing: HEAD 1e702ba, v0.2.81 / code 83, Room schema 10, 235 main + 180 JVM-test + 15 instrumentation Kotlin files, 979 @Test methods, 74 registered + 10 engine-handled actions, 7 context families, targetSdk 37.

Confidence labels: Verified = read in this tree or a primary source during this pass. Likely = corroborated by secondary sources only. Needs live validation = requires a device, emulator, or external account.

Executive Summary

OpenTasker is the most rigorously-engineered app in the FOSS Android automation category, and the survey confirms it holds an axis no competitor holds simultaneously: current target SDK, zero F-Droid anti-features, encrypted storage, SBOM+OSV release gating, a capability contract that resolves unreviewed actions to unsupported, and per-profile match explanations. Every other project in the survey picked one of two failure modes — safe and stalled (Easer: no feature code since 2022, still no run log against a nine-year-old issue; PhoneProfilesPlus: froze targetSdk at 28, ejected from f-droid.org, flagged by Play Protect) or powerful and broken (Klick'r: Android 15 gesture bug blocked since 2025-01; AutoJs6: a 7 GB RAM issue then a maintainer suspension notice; Key Mapper: perpetual Shizuku-bridge breakage). The third path OpenTasker chose is validated. Its cost is authoring expressiveness, and the fix is not more privilege — it is editor-layer and ecosystem-layer power.

The highest-value direction is "why did my automation not run, and where exactly did it break." 129 of 190 analysed Home Assistant forum threads (68%) are debugging, and existing tools fix none of them (TOSEM 2025). Tasker's own tracker has 38 votes on "improved debugging" with no developer response, 26 on "Enhanced Run Log", 25 on undo, 41 on rename-with-reference-update. Apple Shortcuts has no debugger at all. Node-RED has none in core. Nobody has shipped a debugger for automation on a phone, and OpenTasker already owns every hard prerequisite: action-level traces, EngineHealthReader, ExecutionEnvelope, PreflightRunner, EditHistoryEntity(previousJson,nextJson), and ExecutionAdmissionController.

Top opportunities, priority order:

  1. Fix the undo/redo corrupt-payload write — the only real correctness bug found; it violates the repo's own fail-closed doctrine and silently persists undecodable JSON. (Verified)
  2. Close the release-truth gaptools/release-truth.json's dependencies block is generated but never asserted and is currently stale; the generator would regress engineHandledActions 10→7 if re-run. (Verified)
  3. Make flow.try retry honest — the editor exposes max_attempts for every try block, but only 1 of 74 actions is IDEMPOTENT, so it is a no-op for the other 73. (Verified)
  4. Harden dependency verification — 1396 checksums all carry origin="Generated by Gradle" with verify-signatures=false: trust-on-first-fetch, not independent verification. (Verified)
  5. Inter-profile causal-loop detectionMAX_SUBTASK_DEPTH guards intra-run recursion only; an A→B→A profile cycle is invisible to it and surfaces only as an unexplained rate-limiter storm. (Verified)
  6. ApplicationExitInfo correlation + missed-trigger reconciliation — turns the category's #1 complaint (OEM process killing, endorsed by Tasker's own author) into a machine answer. Zero occurrences in the tree today. (Verified)
  7. Typed output→input variable picker — the single largest authoring-UX delta in the survey (vFlow, Apple Shortcuts). Editor-only change; storage stays {{ }} text. (Verified)
  8. Variable rename/delete reference safetyAutomationReferenceIndex.OwnerKind covers PROFILE, TASK, SCENE but not VARIABLE; the machinery exists. (Verified)
  9. Publish docs/, unpublish the raw research dump.gitignore's *.md leaves the whole docs/ tree untracked (README's link 404s on GitHub) while a 60 KB raw agent transcript is committed. (Verified)
  10. Held-execution replay + simulate-trigger — converts bounded admission from silent data loss into a reviewable queue, and closes the debug loop.

Product Map

Core workflows

  • Author: profile = context predicates (7 families, optional ALL/ANY/NOT tree) → enter/exit tasks = ordered ActionSpec list with per-action condition and continueOnError, plus engine flow nodes (if/else, for each, try/catch, task.run, stop).
  • Execute: AutomationService (FGS specialUse|location) → ProfileMatcherExecutionAdmissionController (8 global / 2 per-profile active, 32/8 burst per 10 s, 3-strike 60 s circuit) → TaskRunnerrun_logs with action traces.
  • Inspect: Context Inspector (live source health, Loading/Ready/Stale/Error, per-profile match explanation), Run Log (keyset paging, SQL filters, redacted export), Diagnostics (heartbeat, standby bucket, FGS type, exact alarms, watchdog), live in-flight view with per-run cancellation.
  • Preflight: PreflightRunner — side-effect-free preview with synthetic event variables, expanded inputs, branch decisions, setup gaps, blockers.
  • Transfer: OpenTasker JSON bundles (schema v2, deterministic ordering, budget-capped preflight, disabled-by-default install), Tasker XML import/export, encrypted .otbackup v2, offline share manifests.

Personas

  • Privacy-conscious users who want automation without an account or analytics.
  • Tasker/MacroDroid/Automate migrants who want the familiar vocabulary with safer defaults and open distribution.
  • Power users combining Termux, HTTP, MQTT, variables, scenes, and plugins, expecting secrets and destructive actions to stay bounded.
  • F-Droid packagers needing reproducible builds and truthful metadata.
  • Out of the safe core: unrestricted screen-driving agents, hidden APIs, root assumptions, arbitrary shell/JS by default, server-backed sharing.

Platform and distribution

Single Gradle app module plus :baselineprofile. No flavors — distribution is a -PopenTaskerDistribution=standard|fdroid|play property driving BuildConfig and six manifest placeholders (Play strips SMS/phone-state). Release signing is env-var driven; absent keys produce the unsigned APK F-Droid consumes. No .github/ — CI was deliberately deleted (3d88f83); the gate is tools/verify-local-release.ps1:app:localQualityGate (lint, androidTest compile, Room schema drift, release truth, resolved-dependency policy, CycloneDX SBOM + OSV batch query, JVM test floor, seeded-failure proof, 16 KB native alignment, performance evidence, documentation truth) plus Play and F-Droid release builds and a configuration-cache reuse assertion.

Key integrations and data flows

Locale/Tasker plugin host (setting dispatch, condition query, request-query events) and Locale setting plugin (LocaleSettingEditActivity + LocaleSettingFireReceiver are exported, so Tasker/MacroDroid can already run OpenTasker tasks — but there is no EDIT_CONDITION/QUERY_CONDITION component, so they cannot use OpenTasker's contexts as conditions). Signature-permission external intent API with async protocol v2. UnifiedPush-shaped event=push broadcast bridge. In-app MQTT 3.1.1 client over platform sockets. Home Assistant outbound webhook. Termux RUN_COMMAND with a SHA-256 allowlist. Shizuku status detection only — ShizukuShellRunner.hasPrivilegedTransport() is a hard-coded false, so the entire elevated pathway is honest scaffolding that cannot reach Ready.

Competitive Landscape

vFlow (GPL-2.0, 1209★, v1.5.2 2026-05-23) — the architecture to learn from

Learn: one uniform ActionModule abstraction for triggers, actions, and control flow; every module self-describes InputDefinition/OutputDefinition/permissions/summary; typed magic variables where step N's typed output is selectable as step N+1's input so the editor only offers compatible sources; dynamic parameter visibility; an optional per-module custom-editor escape hatch; an aggregate permission request before a workflow runs. Avoid: minSdk 29 (drops Android 8/9); a side-loaded privileged vFlowCore.dex (wrecks reproducibility); a single 21-comment mega-issue as the backlog; and its own issue #15 — a permission health check that reports healthy on first launch, i.e. fail-open, the exact trap OpenTasker's contract avoids.

Easer (GPL-3.0, 969★) — the cautionary tale

Learn: the Script/Profile split makes reuse first-class; "dynamics" flow event payloads into operations; out-of-process Remote Skill plugin APKs over AIDL with a published example repo; rare operations nothing else has (operation_wireguard, raw TCP/UDP send, event_tcp_trip round-trip probe, script-controls-script). Avoid: no run log — issue #21 "Add log functionality to aid with debugging" has been open since 2017-07-07. Last feature commit 2022-08-27; 243 open issues serving as the only roadmap. Declaring a plugin API "unstable" and then freezing it.

PhoneProfilesPlus (Apache-2.0, 647★, 7.1.2 2025-09-24) — event semantics worth copying

Learn: event priority, paired profile-on-start/profile-on-end, automatic state restoration when an event ends, start/end delay, an "ignore manual activation" flag, and a helper-APK privilege split (PhoneProfilesPlusExtender, PPPPutSettings) that keeps privileged capability outside the main app's threat boundary. Avoid: the whole reason it can do that — targetSdk frozen at 28, which costs it Play Protect trust and f-droid.org inclusion and forces AndroidHiddenApiBypass + DexMaker. Its issue #154 also shows users find "Event" the wrong word for a level condition; OpenTasker's "context" is the better name.

Automation, by Jens Schröder (GPL-3.0, 1.8.7 2026-03-10) — the only actively-released FOSS rule engine on f-droid.org

Learn: 29 triggers / 34 actions, and the six cheap sensor triggers OpenTasker lacks — speed (GPS velocity), activityDetection, proximity, deviceOrientation, roaming, tethering — plus checkVariable as a trigger (variable-change reactivity, not just a condition) and profileActive (one rule's state triggering another). Its fdroiddata entry declares a dedicated fdroidFlavor with a minimal, documented degoogling seam. Avoid: a self-hosted Gitea plus a static HTML "known problems" page instead of a tracker — the project is un-auditable from outside and invisible to contributors.

Home Assistant + n8n + Node-RED — the observability playbook

Learn: HA's per-run traces (step timeline, a graph of the executed path, per-node data and result, the config as it was at execution time, arrows to walk the last 5 runs, bounded by stored_traces) and max + max_exceeded: silent|<level> per automation; n8n's Debug in Editor (pin a failed execution's data and re-run from the failing node), the executions-vs-workflow-history split, and a dedicated error workflow with an Error Trigger; Node-RED's scoped Catch with an explicit "only previously unhandled" fallback tier, a structured error object carrying source.id/type/name/count, and a runtime loop counter with a hard uncatchable cap at 9. Avoid: n8n issue #10763 — a node whose retry succeeded is still reported as errored. That is a test case, not a pattern.

Tasker + MacroDroid — the market signal, not the design

Learn from their trackers, not their code. Tasker's 1,002 open ideas rank: OR-logic in contexts (171 — OpenTasker has it), a separate non-Google share account (54), rename variables with auto-updating references (41), improved debugging (38, no dev response), grouped/collapsible tasks (31), a grace period before state contexts activate (27, marked Planned), enhanced run log (26 — "fails to tell users what exactly went wrong"), undo (25), search (13), plus a standing "source control for Tasker" request for commit/diff/branch. MacroDroid shipped a bare Goto action in 5.62 — evidence that even a "simple" tool's users demand real control flow. Avoid: Tasker's 2025-26 strategy is Shizuku-as-escape-hatch, arbitrary Java with an LLM assistant, and becoming an OEM assistant target. MacroDroid's template store is account-bound with Firebase Analytics + Crashlytics always on and AdMob for non-Pro — and AlexeiCrystal/macrodroid-store-vulnerability documents now-fixed authorization logic that permitted access to any account and any published macro. That is the concrete argument against ever adding a hosted marketplace.

Apple Shortcuts + HTTP Shortcuts — authoring benchmarks

Learn: Shortcuts' Magic Variables (an action's output is implicitly referenceable downstream, the token rendered with the producing action's icon, with an inline type-cast menu) and App Intents' mandatory parameter-summary sentence — the action row is one grammatical sentence for any parameter value. HTTP Shortcuts (MIT, 16 open issues, best hygiene in the survey) contributes client-certificate auth plus certificate pinning, typed prompt-at-runtime variables, and password-protected archive export. Avoid: Shortcuts has no debugger — users abuse modal Show Alert, and a third-party app (Logger) exists purely to be the missing console. HTTP Shortcuts' browser-based Web Editor is a network authoring surface OpenTasker's local-only boundary must not grow.

Security, Privacy, and Reliability

Bugs and risks found

  • Undo/redo persists undecodable payloads. app/src/main/java/com/opentasker/ui/screens/ActiveAutomationViewModel.kt:1346-1351, 1364-1368, 1381-1386. decodeFromString<Task>(targetJson).takeIf { it.id == entityId }?.toEntity() falls back to current.copy(actionsJson = targetJson) on null, writes it inside the transaction, and returns success. TaskDao.kt:43 requires that column to decode as List<ActionSpec>; on the ID-mismatch branch a whole-Task JSON object is written into it. Same shape for contextsJson and elementsJson. No log, no user-visible error, and no distinction between "decode failed" and "wrong entity". Every other stored-payload path fails closed (e2f8786). Verified.
  • Dependency verification is trust-on-first-fetch. gradle/verification-metadata.xml: 824 components, 1396 SHA-256s, every one origin="Generated by Gradle", and <verify-signatures>false</verify-signatures>. No blanket trusted-artifacts (good), but the checksums record whatever was downloaded rather than upstream-published values, and no PGP signature is checked. Verified.
  • flow.try retry is a no-op for 73 of 74 actions. TaskRunner.kt:358-360 gates retry on retrySafety == IDEMPOTENT; Action.kt:51 defaults to NEVER and only HttpRequestAction.kt:184 overrides it. The max_attempts and backoff_ms fields are offered in the editor for every try block regardless. Verified.
  • Release truth has unverified fields. VerifyReleaseTruthTask asserts application, android, and capabilities keys but never touches the dependencies block — which currently claims kotlin 2.3.21, ksp 2.3.7, composeBom 2026.05.00 against libs.versions.toml's 2.4.10 / 2.3.10 / 2026.06.00. Worse, tools/generate-release-truth.ps1 hardcodes engineHandledActions = 7 while the committed file and README say 10 — regenerating today would silently regress the claim. roomSchemaVersion is scraped from a val currentVersion = 10 literal in the build script rather than OPEN_TASKER_DATABASE_SCHEMA_VERSION in AppDatabase.kt:7. Four separate hardcoded copies of the same facts exist (verifyDocumentationTruth 74/7/10, verifyRoomSchema 10, the truth file, the generator). Verified.
  • A documentation gate that covers nothing on a clean checkout. app/build.gradle.kts:754-757 passes CLAUDE.md and docs/research/* through .filter(File::isFile). Those paths are gitignored, so on an F-Droid or fresh-clone build the historical-claim scan silently has zero inputs and always passes. Verified.
  • Inter-profile causal loops are undetected. TaskRunner.kt:475 enforces MAX_SUBTASK_DEPTH for nested task.run, but an action whose effect satisfies another profile's context starts a fresh depth-0 run. Only ExecutionAdmissionController's rate limit and circuit breaker catch it, as an unexplained storm with no named cause. CHI 2019 classifies this as the Infinite Loop bug; Node-RED solves it with a hard uncatchable loop counter. Verified.
  • Kotlin CVE-2026-53914 / GHSA-r937-wjx7-w2jp (CVSS 6.7, published 2026-06-26): unsafe deserialization in Kotlin build-cache metadata, affects before 2.4.20; the tree is on 2.4.10 and 2.4.20 is only at Beta2. Build-time, local, high-privilege — not shipped in the APK. Mitigation now is refusing untrusted remote build caches. Verified.
  • Netty 4.1.93 / 4.1.110 appear in verification-metadata.xml with ~15 2026 advisories, several high. Confirmed not in the shipped APK — unzipping app-release-unsigned.apk yields zero netty strings — so risk is confined to the build classpath, but it will keep tripping SCA scans. Verified.
  • The public repo carries a raw AI agent transcript. docs/research/raw-research-output.txt is tracked (60,688 bytes) and opens with Agent completed. agent_id: l1-research-phase, agent_type: research…. app/build.gradle.kts:755 names CLAUDE.md in committed source. Both violate the standing no-AI-references-in-committed-content rule. Verified.
  • Clean bill elsewhere: OkHttp 5.4.0, jsoup 1.22.2, Okio 3.17.0, kotlinx-serialization 1.11.0, androidx.sqlite/Room — no applicable advisories. Zero TODO/FIXME/HACK/XXX, zero !!, zero Thread.sleep, zero runBlocking, zero GlobalScope in app/src/main. One unlogged empty catch (SettingsActions.kt:352, an idempotent torch-callback unregister) and seven log-only catches, all on receiver or crash-handler boundaries where throwing would be worse. Verified.

Missing guardrails

  • No ApplicationExitInfo use anywhere (getHistoricalProcessExitReasons, API 30+) — the app cannot tell a user why the engine stopped, despite EngineHeartbeatStore already recording that it stopped. Android 17 adds a "MemoryLimiter:AnonSwap" description for the new per-device RAM caps.
  • No missed-trigger reconciliation: nothing records an expected next-fire time to compare against actual fires, so a Doze or OEM-kill gap is invisible after the fact.
  • No profile priority field, so two profiles whose contexts are simultaneously satisfiable and whose tasks write the same setting resolve nondeterministically. CHI 2019 measured this as the worst-accuracy bug class (40.5% correct).
  • No Direct Boot storage (createInDeviceProtectedStorage(), stable in DataStore 1.2.0), so triggers cannot arm before first unlock — matching the widely-reported post-reboot dead zone.
  • StrictMode has detectUnsafeIntentLaunch() but not Android 17's detectImplicitUriPermissionGrant(), the forward detector for Android 18 enforcement on ACTION_SEND URI grants — directly relevant to ShareReceiverActivity and IntentDispatchFlag.GRANT_READ_URI.
  • verifyJvmTestCount floor is 522 against 979 actual tests — nearly half the suite could be deleted without tripping the gate.
  • No coverage measurement (no Kover, no JaCoCo) anywhere in the build.

Recovery and rollback

ExecutionAdmissionController skips are terminal — a circuit trip or capacity rejection discards the triggering event. Zapier's model is better: park it in a HELD state with the captured input and require a human to replay; never auto-replay held work. RunLogPruneWorker prunes by retention with no carve-out for held or user-starred runs, so a dead-letter queue would silently evaporate (n8n's pruning explicitly exempts annotated and non-terminal executions). EditHistoryEntity already stores previousJson/nextJson, which is everything needed for a semantic diff of an edit — currently unused for review.

Architecture Assessment

Module and boundary improvements

  • Two unlinked action registries. core/RuntimeRegistries.kt (74 hand-maintained constructor calls) and core/actions/ActionMetadata.kt (1267 lines of editor metadata keyed by the same strings) have no compile-time link; metadata key drift is a documented past bug class (v0.2.62). Adding one action currently touches nine files (RuntimeRegistries, ActionMetadata, ActionCapabilities, ActionArgumentSensitivity, action_catalog_strings.xml, release-truth.json, a test, CHANGELOG, README) — and the commit history confirms this is the dominant change shape. A single declaration site is the highest-leverage refactor available.
  • ui/screens is a god-package: 29 files, the four largest files in the repo (ActiveAutomationViewModel.kt 1535, ActiveAutomationUi.kt 1496, PermissionOnboardingScreen.kt 1301, ActiveAutomationLists.kt 1013), with ~6300 lines forming one logical screen split by file size to satisfy a self-imposed 1500-line cap rather than by responsibility. It has been split twice and is churning again (58 combined changes in 200 commits). Klick'r's core/{common,dumb,smart} + feature/{backup,notifications,smart-debugging,tutorial,…} layout proves the shape works at this size.
  • Vestigial naming: OpenTaskerApp_NoHilt.kt encodes a removal completed long ago, and the name is asserted in a baseline-profile check at app/build.gradle.kts:723, so renaming requires touching a gate.
  • Four Quick Settings tile services are one-line subclasses needing 16 manifest lines each; a fifth slot means editing the manifest again.
  • Three HTTP actions (http.request, http.get, http.post) live in one file with the latter two as thin aliases, inflating the 74 count.

Refactor candidates

  • app/src/main/java/com/opentasker/ui/screens/ActiveAutomationViewModel.kt — split by domain (profiles / tasks / edit history / import), not by line count.
  • app/src/main/java/com/opentasker/core/actions/ActionMetadata.kt — fold into the action declaration.
  • app/src/main/java/com/opentasker/core/actions/TemporaryStateAction.kt — hosts TemporaryStateRevertWorker inside a 300-line action file, away from the other two workers in core/engine.
  • app/build.gradle.kts — 900+ lines with twelve custom verification tasks and four hardcoded capability literals; extract to buildSrc or a convention plugin so the literals have one home.

Test and documentation gaps

  • Zero test coverage: core/scheduling (ExactAlarmSupport), core/resilience (GracefulDegradation), automation/receiver (TimeEventReceiver), ui/utils. Thin relative to size: core/model (8 sources, 2 tests) and ui/screens (29 sources including the four largest files, 21 JVM tests).
  • .gitignore's *.md makes the entire docs/ tree untracked. README links docs/EXTERNAL_INTENTS.md at line 112, which 404s on GitHub. Fifteen genuinely useful docs (ARCHITECTURE, FDROID_READINESS, TEMPLATE_EXPRESSIONS, TERMUX_SCRIPTING, OPEN_JSON_BUNDLE, SHIZUKU, …) are invisible to contributors and F-Droid reviewers, while the raw research transcript is the one thing that is published. Exactly inverted. RESEARCH.md and ROADMAP.md are also tracked despite AGENTS.md stating README is the only committed .md (they predate the ignore rule).
  • CLAUDE.md is two releases stale (says v0.2.79 / schema v8 / 43 actions vs 0.2.81 / v10 / 74), still lists the deleted .github/workflows/build-release.yml as a Key File, still documents Hilt as in-stack, and its gotcha #9 describes an AGP 8.9.1 / Compose BOM 2025.07.00 toolchain two upgrades out of date — its dependency-ceiling claims should not be trusted without re-verification.
  • Roadmap_Blocked.md lists UnifiedPush event=push (RD32), MQTT publish (RD34), and the Home Assistant webhook bridge as blocked, but README documents all three as shipped. Those entries should be deleted.
  • i18n is effectively absent: 1,789 base strings; values-es has 34 (1.9%); the other twelve locale dirs contain only an empty <resources/> with a comment. No android:localeConfig and no androidResources { generateLocaleConfig = true }, so the Android 13+ per-app language picker is unavailable and twelve empty resource dirs ship in every APK.
  • No update path for GitHub-release installs (no UpdateChecker, no release-feed poll); F-Droid installs are covered by F-Droid.

Rejected Ideas

  • Accessibility screen-driving / image-match automation. Klick'r issue #599 (Android 15 gestures stop randomly) has been labeled blocked since 2025-01-16 and Key Mapper's Shizuku System Bridge breaks on every wireless-debugging re-prompt (#2026, #2071). ROADMAP already carries a P3 evaluation item — this evidence supports "no" for the default engine, and the eval should record it rather than a new item being filed.
  • JS/Lua scripting sandbox. AutoJs6 issue #372 is a 7 GB RAM blowup, #167 is scheduled scripts freezing, and #407 is the maintainer's own indefinite project-suspension notice (2025-05-30). Three of six Auto.js forks are NOASSERTION-licensed, which blocks F-Droid outright.
  • Cloud template marketplace or hosted share account. AlexeiCrystal/macrodroid-store-vulnerability documents now-fixed authorization logic in MacroDroid's store that permitted access to any account and any published macro. Offline manifests plus QR/text import already deliver the sharing value without moderation, account, or breach obligations.
  • Browser-based web editor (HTTP Shortcuts). A network authoring surface contradicts the local-only boundary.
  • RangingManager / UWB proximity trigger. Already in Roadmap_Blocked.md, blocked on ranging-capable hardware. Do not re-file.
  • Health Connect, Weblate, F-Droid reproducibility evidence, TalkBack sweep, Shizuku device evidence, API 37 device matrix, background audio, BAL IntentSender opt-in. All already in Roadmap_Blocked.md with correct blockers. Note specifically that the BAL entry has already verified MODE_BACKGROUND_ACTIVITY_START_ALLOWED is unused anywhere in the tree, so there is nothing to migrate away from.
  • Glance migration now. Answers the existing P3 eval: Glance stable is still 1.1.1 (2024-10-16) and 1.2.0 has sat at rc01 since 2025-12-03 with no stable in eight months; glance-wear-tiles is deprecated. Wait for 1.2.0 stable.
  • Navigation3 migration now. Answers the existing P3 eval: core is stable (1.0.0 2025-11-19, now 1.1.5), but deep links only arrived in 1.2.0-alpha03 and those factory functions already broke at alpha05; result passing is alpha-only. Minimum bar: Navigation3 1.2.0 stable.
  • Ambient-noise-level trigger (Automation has one). Requires a continuously-held RECORD_AUDIO grant for a trigger — irreconcilable with the privacy posture. The other five sensor triggers from that project are fine.
  • Multi-user / work-profile automation. Android 17 blocks cross-profile loopback outright, work-profile automation needs device-policy standing the app deliberately does not hold, and a local-first single-device engine has no multi-tenant story. Revisit only if a work-profile trigger (which MacroDroid ships) is requested by real users.
  • Freezing targetSdk to keep privileged behaviour (PhoneProfilesPlus's choice). It cost that project f-droid.org inclusion and Play Protect trust.
  • Obfuscated evasion builds (Klick'r ships one). Destroys reproducibility and contradicts fail-closed honesty.
  • Deleting docs/archive/ui-snapshots/*.kt.bak. Fifteen dead files from an abandoned UI generation, but they are .bak and already untracked, so they cost nothing but disk. Housekeeping, not roadmap work.
  • Room 3 migration and hosted CI — unchanged from the prior pass; Room 3's package and codegen change is a dedicated project, and CI was deliberately removed in 3d88f83.

Sources

Android platform and APIs

AndroidX, Kotlin, build, and supply chain

Distribution

FOSS competitors

Commercial competitors and community signal

Adjacent workflow systems

Academic

Open Questions

  • Debug-capture boundary. Deep debuggability requires showing the real values that flowed through each action, which is in direct tension with the redaction guarantee that makes OpenTasker worth choosing. Is debug capture an ephemeral, per-task, opt-in elevated capability with its own visible state (recommended) or a logging verbosity level? This decision gates the entire debugger track and cannot be answered by inspecting the code.
  • Plugin SDK trust model. If out-of-process action/condition plugins ship, what proves a plugin APK is trustworthy without a marketplace — signature pinning by the user at install, a capability manifest reviewed like a bundle import, or both? Easer's frozen "unstable" API is the failure mode to avoid.
  • F-Droid signing key custody. Google's Developer Verification enforces from 2026-09-30 in Brazil, Indonesia, Singapore, and Thailand, with global rollout in 2027; F-Droid is explicitly out of scope "yet". Package-name registration requires proving ownership with an APK signed by the private key, and Limited Distribution caps at 20 devices. Who owns the long-lived signing key, and is it backed up as if package-name ownership depends on it?
  • Run-log growth budget. Traces with per-step variable snapshots, replayable payloads, and held executions will make run_logs the largest table and the hottest write path on a battery-sensitive foreground service. What per-profile trace cap and total-size ceiling are acceptable before the feature is built, rather than retrofitted?
  • Whether RESEARCH.md and ROADMAP.md should stay tracked. They contradict the repo's stated rule that README is the only committed .md. A concurrent Codex session committed both in 50d998b on 2026-08-02, so two agents are now writing the same two files — the maintainer needs to pick one owner before the next pass.