Skip to content

refactor(rust): modularize tc_helper, add typed errors, surface task …#649

Open
BrawlerXull wants to merge 9 commits into
CCExtractor:mainfrom
BrawlerXull:feat/rust-ffi-overhaul
Open

refactor(rust): modularize tc_helper, add typed errors, surface task …#649
BrawlerXull wants to merge 9 commits into
CCExtractor:mainfrom
BrawlerXull:feat/rust-ffi-overhaul

Conversation

@BrawlerXull

@BrawlerXull BrawlerXull commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

…attributes

Overhauls the Rust FFI bridge (Deliverable 2):

Build config

  • Pin Cargo.toml to edition 2021 for broader Rust/flutter_rust_bridge compatibility; add the thiserror dependency.
  • Add rust/build.rs: rerun-on-source-change tracking, target-platform detection (exposed via the TC_HELPER_PLATFORM compile-time env), and an opt-in (FRB_CODEGEN=1) binding-regeneration trigger.

Modularization & typed errors

  • Extract the duplicated storage-open boilerplate into storage.rs, the task serializer into serialize.rs, and a thiserror-based TcHelperError into utils/error.rs. api.rs is now a thin FFI layer of delegators.
  • Remove every .unwrap() panic; each FFI entry point returns Result<_, String> so flutter_rust_bridge surfaces failures to Dart as catchable exceptions with a real message, instead of relying on panics.

Surfaced task attributes

  • The serializer now emits annotations, dependencies (depends), is_blocked, is_blocking, and recur, which the Dart TaskForReplica model previously anticipated but never received. TaskForReplica gains those fields + parsing (reusing the existing Annotation model).
  • urgency is intentionally NOT surfaced: TaskChampion 2.0.3 does not compute or store an urgency value on Task, so there is nothing authoritative to expose.

Regenerated the flutter_rust_bridge bindings for the new signatures (getAllTasksJson stays Future; the mutating calls are now Future, throwing on error — all existing Dart callers ignored the old int return).

Deferred (needs mentor coordination + CI cross-compilation, not doable here): purging the tracked android jniLibs *.so binaries. There is currently no Android cargo/cargo-ndk integration, so Gradle bundles the committed .so directly; removing them before automated cross-compilation exists would break Android builds.

cargo build + cargo test: pass. flutter analyze: 0 errors. Full test suite unchanged at +317 -20 (pre-existing headless failures only). Runtime FFI behaviour still needs validation via an on-device build.

Description

Please include a summary of the change and which issue is fixed. List any dependencies that are required for this change.

Fixes #(issue_no)

Replace issue_no with the issue number which is fixed in this PR

Screenshots

Checklist

  • Tests have been added or updated to cover the changes
  • Documentation has been updated to reflect the changes
  • Code follows the established coding style guidelines
  • All tests are passing

Summary by CodeRabbit

  • New Features

    • Task details now show richer replica-backed metadata for replica tasks, including blocked/blocking state, dependencies, recurrence, and formatted annotations.
  • Bug Fixes

    • Replica-backed task detail fields no longer fall back to empty/default values.
    • Restored saved home filter state on startup and improved search behavior when toggled.
    • Guided tours are now more resilient—shown only when targets are available, avoiding unnecessary re-triggers.
  • Chores

    • Pinned the Flutter Rust bridge dependency to a specific version.

…attributes

Overhauls the Rust FFI bridge (Deliverable 2):

Build config
- Pin Cargo.toml to edition 2021 for broader Rust/flutter_rust_bridge
  compatibility; add the thiserror dependency.
- Add rust/build.rs: rerun-on-source-change tracking, target-platform
  detection (exposed via the TC_HELPER_PLATFORM compile-time env), and an
  opt-in (FRB_CODEGEN=1) binding-regeneration trigger.

Modularization & typed errors
- Extract the duplicated storage-open boilerplate into storage.rs, the task
  serializer into serialize.rs, and a thiserror-based TcHelperError into
  utils/error.rs. api.rs is now a thin FFI layer of delegators.
- Remove every .unwrap() panic; each FFI entry point returns Result<_, String>
  so flutter_rust_bridge surfaces failures to Dart as catchable exceptions with
  a real message, instead of relying on panics.

Surfaced task attributes
- The serializer now emits annotations, dependencies (depends), is_blocked,
  is_blocking, and recur, which the Dart TaskForReplica model previously
  anticipated but never received. TaskForReplica gains those fields + parsing
  (reusing the existing Annotation model).
- urgency is intentionally NOT surfaced: TaskChampion 2.0.3 does not compute or
  store an urgency value on Task, so there is nothing authoritative to expose.

Regenerated the flutter_rust_bridge bindings for the new signatures
(getAllTasksJson stays Future<String>; the mutating calls are now Future<void>,
throwing on error — all existing Dart callers ignored the old int return).

Deferred (needs mentor coordination + CI cross-compilation, not doable here):
purging the tracked android jniLibs *.so binaries. There is currently no
Android cargo/cargo-ndk integration, so Gradle bundles the committed .so
directly; removing them before automated cross-compilation exists would break
Android builds.

cargo build + cargo test: pass. flutter analyze: 0 errors. Full test suite
unchanged at +317 -20 (pre-existing headless failures only). Runtime FFI
behaviour still needs validation via an on-device build.
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR updates the Rust task backend, generated Dart/Rust bridge code, and Flutter task and tour UI to carry replica-backed task fields, return stringified task operation results, and use guarded page-tour display. It also adds a source build workflow and adjusts iOS framework metadata.

Changes

Rust API and Dart bridge taskflow updates

Layer / File(s) Summary
Error, storage, build, and module wiring
rust/src/utils/error.rs, rust/src/utils/mod.rs, rust/src/storage.rs, rust/src/lib.rs, rust/Cargo.toml, rust/build.rs, pubspec.yaml
Adds TcHelperError, open_replica, module exports, crate and dependency changes, build script support, and the pinned bridge dependency.
Task JSON serialization
rust/src/serialize.rs
Adds task_to_json, deriving annotations, depends, tags, is_blocked, and is_blocking into the JSON shape returned to Flutter.
Rust task API rewrites
rust/src/api.rs
Changes task entry points to Result return types, uses the new replica and serializer helpers, tightens UUID and tag parsing, and updates task/sync handling and tests.
Generated Rust FFI updates
rust/src/frb_generated.rs
Updates generated wire handlers to propagate string errors and removes AnyhowException/i8 SSE codec implementations.
Generated Dart bridge updates
lib/rust_bridge/api.dart, lib/rust_bridge/frb_generated.dart, lib/rust_bridge/frb_generated.io.dart, lib/rust_bridge/frb_generated.web.dart
Updates Dart bridge wrappers to Future<void> for task mutations and removes the corresponding error and integer codec helpers.

Flutter task and tour UI updates

Layer / File(s) Summary
Home replica filtering and rendering
lib/app/modules/home/controllers/home_controller.dart, lib/app/modules/home/views/home_page_body.dart, lib/app/modules/home/views/show_tasks_replica.dart
Initializes persisted home filters, snapshots replica tasks in the page body, and removes the nested Obx from the replica list builder.
Replica task details fields
lib/app/v3/champion/models/task_for_replica.dart, lib/app/modules/taskc_details/controllers/taskc_details_controller.dart, lib/app/modules/taskc_details/views/taskc_details_view.dart
Adds replica-backed task fields to TaskForReplica, copies them through the task details controller, and renders the new read-only replica rows in the task details view.
Guarded page tours
lib/app/tour/safe_tour.dart, lib/app/modules/manageTaskServer/controllers/manage_task_server_controller.dart, lib/app/modules/profile/controllers/profile_controller.dart
Adds a guarded tour helper and switches the manage-task-server and profile tours to use it after checking seen status.

Build and platform packaging

Layer / File(s) Summary
Source build workflow and iOS slice order
.github/workflows/build-tc-helper.yml, ios/tc_helper.xcframework/Info.plist
Adds the build-tc-helper workflow for native Android and Flutter release builds, and swaps the xcframework slice order in the iOS plist.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FlutterApp
  participant DartBridge as lib/rust_bridge
  participant RustApi as rust/src/api.rs
  participant Storage as open_replica
  participant Serializer as task_to_json

  FlutterApp->>DartBridge: getAllTasksJson(taskdbDirPath)
  DartBridge->>RustApi: crateApiGetAllTasksJson(...)
  RustApi->>Storage: open_replica(taskdb_dir_path)
  Storage-->>RustApi: Replica or TcHelperError
  RustApi->>Serializer: task_to_json(task)
  Serializer-->>RustApi: JSON Value
  RustApi-->>DartBridge: Result<String, String>
  DartBridge-->>FlutterApp: JSON payload

  FlutterApp->>DartBridge: updateTask(uuidSt, taskdbDirPath, map)
  DartBridge->>RustApi: crateApiUpdateTask(...)
  RustApi->>Storage: open_replica(taskdb_dir_path)
  RustApi->>RustApi: parse UUID, apply tag/status updates, commit
  RustApi-->>DartBridge: Result<(), String>
  DartBridge-->>FlutterApp: void completion
Loading
sequenceDiagram
  participant Controller as Page controller
  participant SaveTourStatus
  participant SafeTour as safeShowTour
  participant TutorialCoachMark

  Controller->>SaveTourStatus: get...TourStatus()
  SaveTourStatus-->>Controller: seen flag
  Controller->>SafeTour: safeShowTour(...)
  SafeTour->>TutorialCoachMark: show() when mounted and targets exist
  TutorialCoachMark-->>SafeTour: shown or throws
  SafeTour->>SaveTourStatus: markSeen() on skip/failure
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description gives a strong summary, but it omits the required fixed issue number and doesn't fill the checklist/template sections. Add a ## Fixes #<issue_no> section, fill the checklist items, and include screenshots only if applicable.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main changes: Rust bridge refactor, typed errors, and surfaced task attributes, though it is slightly truncated.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/app/modules/taskc_details/controllers/taskc_details_controller.dart (1)

263-324: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

saveTask drops depends, recur, and annotations when saving a replica task.

The controller initializes these fields from the replica model (lines 85-88) and they are user-editable, but the modifiedTask constructed in saveTask (lines 267-324) does not pass them to the TaskForReplica constructor. User edits to these fields are silently lost on save.

🐛 Proposed fix
       priority: priority.string.isNotEmpty ? priority.string : null,
       project: project.string != 'None' ? project.string : null,
+      depends: depends.isNotEmpty ? depends.toList() : null,
+      recur: recur.string.isNotEmpty ? recur.string : null,
+      annotations: annotations.isNotEmpty ? annotations.toList() : null,
     );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/app/modules/taskc_details/controllers/taskc_details_controller.dart`
around lines 263 - 324, The saveTask path for TaskForReplica is omitting
user-editable fields, so edits to depends, recur, and annotations are lost when
building modifiedTask. Update the TaskForReplica construction in saveTask to
carry through the current values for those properties, using the same controller
fields that are initialized earlier in taskc_details_controller.dart, alongside
the existing status, description, tags, and date fields.
🧹 Nitpick comments (1)
lib/app/v3/champion/models/task_for_replica.dart (1)

182-183: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

hashCode doesn't include the new fields checked by operator ==.

While the hashCode/equals contract is technically maintained (equal objects will still produce equal hashes), omitting isBlocked, isBlocking, depends, and recur from the hash causes excessive collisions in hash-based collections like Set and Map keys.

♻️ Proposed fix
-  int get hashCode => Object.hash(modified, due, status, description, uuid,
-      priority, tags == null ? 0 : tags.hashCode, start, wait);
+  int get hashCode => Object.hash(
+      modified, due, status, description, uuid, priority,
+      tags == null ? 0 : tags.hashCode, start, wait,
+      isBlocked, isBlocking,
+      depends == null ? 0 : depends.hashCode,
+      recur,
+      annotations == null ? 0 : annotations.hashCode);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/app/v3/champion/models/task_for_replica.dart` around lines 182 - 183, The
TaskForReplica hashCode implementation is missing the new fields that operator
== compares, which can cause avoidable hash collisions in Sets and Maps. Update
TaskForReplica.hashCode to include isBlocked, isBlocking, depends, and recur
alongside the existing properties so it stays aligned with operator ==. Keep the
change localized to the hashCode getter in TaskForReplica and preserve the
current hashing pattern for the other fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/app/v3/champion/models/task_for_replica.dart`:
- Around line 174-178: `TaskForReplica.operator ==` is missing the `annotations`
field, so equality can incorrectly treat two tasks as the same. Update the
equality check in `TaskForReplica` to include `annotations` alongside the
existing fields (`priority`, `isBlocked`, `isBlocking`, `depends`, `recur`), and
make sure the corresponding `hashCode` logic stays consistent with that change.

---

Outside diff comments:
In `@lib/app/modules/taskc_details/controllers/taskc_details_controller.dart`:
- Around line 263-324: The saveTask path for TaskForReplica is omitting
user-editable fields, so edits to depends, recur, and annotations are lost when
building modifiedTask. Update the TaskForReplica construction in saveTask to
carry through the current values for those properties, using the same controller
fields that are initialized earlier in taskc_details_controller.dart, alongside
the existing status, description, tags, and date fields.

---

Nitpick comments:
In `@lib/app/v3/champion/models/task_for_replica.dart`:
- Around line 182-183: The TaskForReplica hashCode implementation is missing the
new fields that operator == compares, which can cause avoidable hash collisions
in Sets and Maps. Update TaskForReplica.hashCode to include isBlocked,
isBlocking, depends, and recur alongside the existing properties so it stays
aligned with operator ==. Keep the change localized to the hashCode getter in
TaskForReplica and preserve the current hashing pattern for the other fields.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ccbe89b7-140e-4862-8aa3-579e110db3fd

📥 Commits

Reviewing files that changed from the base of the PR and between f058b4a and 09040e1.

⛔ Files ignored due to path filters (1)
  • rust/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • lib/app/modules/taskc_details/controllers/taskc_details_controller.dart
  • lib/app/v3/champion/models/task_for_replica.dart
  • lib/rust_bridge/api.dart
  • lib/rust_bridge/frb_generated.dart
  • lib/rust_bridge/frb_generated.io.dart
  • lib/rust_bridge/frb_generated.web.dart
  • rust/Cargo.toml
  • rust/build.rs
  • rust/src/api.rs
  • rust/src/frb_generated.rs
  • rust/src/lib.rs
  • rust/src/serialize.rs
  • rust/src/storage.rs
  • rust/src/utils/error.rs
  • rust/src/utils/mod.rs
💤 Files with no reviewable changes (1)
  • lib/rust_bridge/frb_generated.io.dart

Comment on lines +174 to +178
other.priority == priority &&
other.isBlocked == isBlocked &&
other.isBlocking == isBlocking &&
_listEquals(other.depends, depends) &&
other.recur == recur;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

annotations is missing from operator ==.

Two TaskForReplica objects with different annotations will be considered equal, which can cause incorrect behavior in collections and diffing logic.

🐛 Proposed fix
         _listEquals(other.depends, depends) &&
-        other.recur == recur;
+        other.recur == recur &&
+        _listEquals(other.annotations, annotations);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
other.priority == priority &&
other.isBlocked == isBlocked &&
other.isBlocking == isBlocking &&
_listEquals(other.depends, depends) &&
other.recur == recur;
other.priority == priority &&
other.isBlocked == isBlocked &&
other.isBlocking == isBlocking &&
_listEquals(other.depends, depends) &&
other.recur == recur &&
_listEquals(other.annotations, annotations);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/app/v3/champion/models/task_for_replica.dart` around lines 174 - 178,
`TaskForReplica.operator ==` is missing the `annotations` field, so equality can
incorrectly treat two tasks as the same. Update the equality check in
`TaskForReplica` to include `annotations` alongside the existing fields
(`priority`, `isBlocked`, `isBlocking`, `depends`, `recur`), and make sure the
corresponding `hashCode` logic stays consistent with that change.

@MabudAlam

Copy link
Copy Markdown
Member

@CodeRabbit full review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
lib/app/v3/champion/models/task_for_replica.dart (1)

174-178: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

annotations is still missing from operator ==.

This was flagged in a previous review and remains unaddressed. Two TaskForReplica objects with different annotations will compare as equal, which can cause incorrect behavior in collections and diffing logic.

🐛 Proposed fix
         _listEquals(other.depends, depends) &&
-        other.recur == recur;
+        other.recur == recur &&
+        _listEquals(other.annotations, annotations);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/app/v3/champion/models/task_for_replica.dart` around lines 174 - 178, The
TaskForReplica equality check in operator == is still missing annotations, so
objects with different annotations can compare equal. Update the equality
comparison in TaskForReplica’s operator == to include annotations alongside the
other compared fields, and make sure the corresponding hashCode logic in the
same class stays consistent with that field.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@lib/app/v3/champion/models/task_for_replica.dart`:
- Around line 174-178: The TaskForReplica equality check in operator == is still
missing annotations, so objects with different annotations can compare equal.
Update the equality comparison in TaskForReplica’s operator == to include
annotations alongside the other compared fields, and make sure the corresponding
hashCode logic in the same class stays consistent with that field.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3c6d86ff-560a-4fdc-8bf9-3957ebe796f2

📥 Commits

Reviewing files that changed from the base of the PR and between f058b4a and 09040e1.

⛔ Files ignored due to path filters (1)
  • rust/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • lib/app/modules/taskc_details/controllers/taskc_details_controller.dart
  • lib/app/v3/champion/models/task_for_replica.dart
  • lib/rust_bridge/api.dart
  • lib/rust_bridge/frb_generated.dart
  • lib/rust_bridge/frb_generated.io.dart
  • lib/rust_bridge/frb_generated.web.dart
  • rust/Cargo.toml
  • rust/build.rs
  • rust/src/api.rs
  • rust/src/frb_generated.rs
  • rust/src/lib.rs
  • rust/src/serialize.rs
  • rust/src/storage.rs
  • rust/src/utils/error.rs
  • rust/src/utils/mod.rs
💤 Files with no reviewable changes (1)
  • lib/rust_bridge/frb_generated.io.dart

…rust_bridge to exact 2.11.1

Adds test_dependencies_and_annotations_surface asserting depends[]/is_blocked/is_blocking and annotation entry(RFC3339)+description. Pins flutter_rust_bridge (was ^2.11.1) to avoid resolving 2.12.0, which crashes at RustLib.init().
The committed .so predated the serializer overhaul (Feb build vs June source), so the app ran stale native code. Rebuilt via cargo-ndk. Stopgap until CI compiles from source (see build-tc-helper.yml).
- home_page_body: reactively read tasksFromReplica in the Obx so the replica list rebuilds when the async FFI fetch completes (was empty: taskReplica flips true before the list is populated).
- task detail: surface annotations/depends/is_blocked/is_blocking/recur (read-only) for replica tasks.
- safe_tour: guard tutorial_coach_mark.show() against unmounted target keys (fixes recurring 'obtain target position (null)' FormatException); applied to profile + manage-task-server tours.
Regenerates the native lib from rust/ on each run so it can't go stale; builds the APK against it. Unverified on CI. armeabi-v7a left as a documented TODO (needs aws-lc-sys bindgen).
The Taskchampion (replica) home never showed tasks despite the FFI returning them. Four compounding bugs:
- show_tasks_replica wrapped its body in an Obx that read no observable -> ObxError -> blank error widget. Reactivity now lives in the parent (home_page_body) Obx, so this is a plain build.
- home_controller.onInit never initialized pendingFilter/waitingFilter from their persisted values, so pendingFilter defaulted false and the list filtered for completed tasks only, hiding all pending ones.
- the project filter treated an empty-string projectFilter ('') as an active filter, dropping every task whose project is null.
Verified on-device: replica home lists tasks; detail view surfaces annotations/depends/is_blocked/is_blocking/recur.
…gen; rebuild both ABIs

32-bit ARM has no pre-generated aws-lc-sys bindings, so it needs the crate's 'bindgen' feature (libclang). Scoped that feature to ONLY [target.armv7-linux-androideabi] so arm64/iOS/host keep using pre-generated bindings (forcing bindgen globally broke the iOS link). Rebuilt both Android ABIs from source; CI installs libclang + builds both. Host cargo test green.
tc_helper only ever uses the remote TaskChampion sync server (ServerConfig::Remote). taskchampion's default 'sync' feature also pulled server-aws + server-gcp, dragging in the entire AWS + Google-Cloud SDKs and aws-lc-rs/aws-lc-sys. Switched to default-features=false + [server-sync, bundled] (ureq + ring).

Wins:
- iOS xcframework now builds (aws-lc-sys 0.30.0 failed to cross-compile PQ/kyber for iOS); rebuilt device arm64 + fat simulator from the new Rust.
- No more bindgen hack for 32-bit ARM (aws-lc gone); dropped libcrc_fast entirely.
- libtc_helper.so 29M->6.2M (arm64), 20M->4.7M (v7a); release APK 60M->36M.
- Smaller dependency/attack surface; faster builds; simpler CI (no cmake/ninja/libclang).

Verified: host cargo test (2) pass; both Android ABIs + all 3 iOS targets build; real-server sync returns Ok() (ureq+ring); on-device the replica home lists tasks + detail rows render with the new lib, no RustLib version crash.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
.github/workflows/build-tc-helper.yml (2)

46-50: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Add Rust/cargo caching to avoid reinstalling cargo-ndk and recompiling all dependencies on every run.

cargo install cargo-ndk and the full dependency tree build from scratch each time. Adding Swatinem/rust-cache is a one-liner that caches the cargo registry, target directory, and installed binaries.

♻️ Proposed addition
       - name: Install Rust + Android targets + cargo-ndk
         run: |
           rustup toolchain install stable --profile minimal
           rustup target add aarch64-linux-android armv7-linux-androideabi
           cargo install cargo-ndk --version ^4
+
+      - uses: Swatinem/rust-cache@v2
+        with:
+          workspaces: rust
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-tc-helper.yml around lines 46 - 50, The Rust setup
step in the build workflow is reinstalling cargo-ndk and rebuilding dependencies
on every run, so add Swatinem/rust-cache in the workflow near the Rust
toolchain/cargo-ndk setup. Wire the cache into the same job that runs rustup and
cargo install cargo-ndk so it preserves the cargo registry, target directory,
and installed binaries between runs, reducing repeated downloads and
recompilation.

69-72: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Set if-no-files-found: error on artifact upload.

Without this, the upload step silently succeeds (with a warning) if the APK is missing, masking build failures.

♻️ Proposed fix
       - uses: actions/upload-artifact@v4
         with:
           name: production-apk-from-source
           path: build/app/outputs/flutter-apk/app-production-release.apk
+          if-no-files-found: error
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-tc-helper.yml around lines 69 - 72, The artifact
upload step in the build workflow is too permissive because it can succeed with
only a warning when the APK is missing. Update the `actions/upload-artifact@v4`
step for `production-apk-from-source` to set `if-no-files-found: error` so
missing `app-production-release.apk` fails the job instead of masking the build
issue.
lib/app/modules/taskc_details/views/taskc_details_view.dart (1)

114-152: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

New replica fields bypass the app's localization pattern.

All other detail labels in this view route through SentenceManager(...).sentences.xxx, but the new Blocked/Blocking/Depends/Recur/Annotations labels (and their 'Yes'/'No'/'None' values) are hardcoded English strings. This breaks localization consistency for non-English users.

♻️ Suggested direction
-                  _buildDetail(
-                    context,
-                    'Blocked:',
-                    controller.isBlocked.value ? 'Yes' : 'No',
-                  ),
+                  _buildDetail(
+                    context,
+                    '${SentenceManager(currentLanguage: AppSettings.selectedLanguage).sentences.detailPageBlocked}:',
+                    controller.isBlocked.value
+                        ? SentenceManager(currentLanguage: AppSettings.selectedLanguage).sentences.yes
+                        : SentenceManager(currentLanguage: AppSettings.selectedLanguage).sentences.no,
+                  ),

Apply the same pattern to Blocking/Depends/Recur/Annotations labels and their string values, adding the corresponding keys to SentenceManager.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/app/modules/taskc_details/views/taskc_details_view.dart` around lines 114
- 152, The replica-task detail labels in taskc_details_view are bypassing the
existing localization flow and are hardcoded in English. Update the
Blocked/Blocking/Depends/Recur/Annotations section to use
SentenceManager(...).sentences like the other fields, and add the needed
sentence keys for both labels and values such as Yes/No/None. Keep the
implementation aligned with the existing _buildDetail usage in TaskcDetailsView
so the new replica fields follow the app’s localization pattern.
rust/Cargo.toml (1)

10-23: 📐 Maintainability & Code Quality | 🔵 Trivial

Consider pinning thiserror to the current major version.

This is a new dependency addition, and thiserror 2.x is the current major release. Since there's no existing 1.x usage to migrate away from, starting on 2.x avoids a future breaking-version bump.

Please confirm the latest stable thiserror release and whether 2.x is compatible with the crate's other dependencies (e.g., MSRV via taskchampion's rust-version = "1.81.0").

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/Cargo.toml` around lines 10 - 23, The new thiserror dependency is pinned
to an older major version, so update the Rust dependency declaration in
Cargo.toml to the current stable 2.x line instead of 1.x. Check that the
thiserror release you choose is compatible with the crate’s MSRV and with
taskchampion’s rust-version setting, then keep the dependency declaration
aligned with that compatible 2.x version.
lib/app/tour/safe_tour.dart (1)

32-35: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider logging the swallowed exception for debuggability.

The catch (_) block is intentionally defensive, but silently discarding the exception makes it hard to diagnose why a tour failed to display. A debug-level log would help troubleshooting without changing behavior.

♻️ Optional: add debug logging
   try {
     tutorialCoachMark.show(context: context);
   } catch (_) {
     // Defensive: never let a tour failure bubble up or repeat.
+    debugPrint('safeShowTour: tour show() failed, marking as seen');
     await markSeen?.call();
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/app/tour/safe_tour.dart` around lines 32 - 35, The `catch (_)` in
`safeTour` is swallowing tour failures without any visibility. Update the
exception handler in `safe_tour.dart` to capture the caught error (and stack
trace if available) and emit a debug-level log before calling
`markSeen?.call()`, keeping the defensive behavior unchanged. Use the `safeTour`
function as the location anchor and preserve the current fallback flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/build-tc-helper.yml:
- Line 32: The checkout step in the build-tc-helper workflow persists the
GITHUB_TOKEN by default, which is unsafe for pull_request runs that execute
untrusted code. Update the existing actions/checkout@v4 step to disable
credential persistence by setting persist-credentials to false so the workflow
no longer leaves the token in git config.
- Around line 62-67: The build job in the workflow needs an explicit timeout
guard to prevent runaway Rust/Flutter builds. Update the job that contains the
flutter-action step and the flutter build apk command to set a reasonable
`timeout-minutes` value, using the existing build job definition as the place to
add it. Keep the change focused on the workflow job configuration so the build
is automatically terminated if it hangs.

---

Nitpick comments:
In @.github/workflows/build-tc-helper.yml:
- Around line 46-50: The Rust setup step in the build workflow is reinstalling
cargo-ndk and rebuilding dependencies on every run, so add Swatinem/rust-cache
in the workflow near the Rust toolchain/cargo-ndk setup. Wire the cache into the
same job that runs rustup and cargo install cargo-ndk so it preserves the cargo
registry, target directory, and installed binaries between runs, reducing
repeated downloads and recompilation.
- Around line 69-72: The artifact upload step in the build workflow is too
permissive because it can succeed with only a warning when the APK is missing.
Update the `actions/upload-artifact@v4` step for `production-apk-from-source` to
set `if-no-files-found: error` so missing `app-production-release.apk` fails the
job instead of masking the build issue.

In `@lib/app/modules/taskc_details/views/taskc_details_view.dart`:
- Around line 114-152: The replica-task detail labels in taskc_details_view are
bypassing the existing localization flow and are hardcoded in English. Update
the Blocked/Blocking/Depends/Recur/Annotations section to use
SentenceManager(...).sentences like the other fields, and add the needed
sentence keys for both labels and values such as Yes/No/None. Keep the
implementation aligned with the existing _buildDetail usage in TaskcDetailsView
so the new replica fields follow the app’s localization pattern.

In `@lib/app/tour/safe_tour.dart`:
- Around line 32-35: The `catch (_)` in `safeTour` is swallowing tour failures
without any visibility. Update the exception handler in `safe_tour.dart` to
capture the caught error (and stack trace if available) and emit a debug-level
log before calling `markSeen?.call()`, keeping the defensive behavior unchanged.
Use the `safeTour` function as the location anchor and preserve the current
fallback flow.

In `@rust/Cargo.toml`:
- Around line 10-23: The new thiserror dependency is pinned to an older major
version, so update the Rust dependency declaration in Cargo.toml to the current
stable 2.x line instead of 1.x. Check that the thiserror release you choose is
compatible with the crate’s MSRV and with taskchampion’s rust-version setting,
then keep the dependency declaration aligned with that compatible 2.x version.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2969d739-f44d-42ca-aa86-1f77e6bbde01

📥 Commits

Reviewing files that changed from the base of the PR and between 09040e1 and d8d7fb0.

⛔ Files ignored due to path filters (5)
  • android/app/src/main/jniLibs/arm64-v8a/libcrc_fast-b3182f249ae653b7.so is excluded by !**/*.so
  • android/app/src/main/jniLibs/arm64-v8a/libtc_helper.so is excluded by !**/*.so
  • android/app/src/main/jniLibs/armeabi-v7a/libcrc_fast-c8bd19aec5c73f3a.so is excluded by !**/*.so
  • android/app/src/main/jniLibs/armeabi-v7a/libtc_helper.so is excluded by !**/*.so
  • rust/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • .github/workflows/build-tc-helper.yml
  • ios/tc_helper.xcframework/Info.plist
  • ios/tc_helper.xcframework/ios-arm64/tc_helper.framework/tc_helper
  • ios/tc_helper.xcframework/ios-arm64_x86_64-simulator/tc_helper.framework/tc_helper
  • lib/app/modules/home/controllers/home_controller.dart
  • lib/app/modules/home/views/home_page_body.dart
  • lib/app/modules/home/views/show_tasks_replica.dart
  • lib/app/modules/manageTaskServer/controllers/manage_task_server_controller.dart
  • lib/app/modules/profile/controllers/profile_controller.dart
  • lib/app/modules/taskc_details/controllers/taskc_details_controller.dart
  • lib/app/modules/taskc_details/views/taskc_details_view.dart
  • lib/app/tour/safe_tour.dart
  • pubspec.yaml
  • rust/Cargo.toml
  • rust/src/api.rs
✅ Files skipped from review due to trivial changes (1)
  • ios/tc_helper.xcframework/Info.plist
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/app/modules/taskc_details/controllers/taskc_details_controller.dart
  • rust/src/api.rs

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Set persist-credentials: false on checkout.

The actions/checkout@v4 action persists the GITHUB_TOKEN in .git/config by default. Since this workflow runs on pull_request and checks out untrusted PR code, disabling credential persistence eliminates a potential token-exfiltration vector.

🛡️ Proposed fix
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@v4
+        with:
+          persist-credentials: false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
persist-credentials: false
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 32-32: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-tc-helper.yml at line 32, The checkout step in the
build-tc-helper workflow persists the GITHUB_TOKEN by default, which is unsafe
for pull_request runs that execute untrusted code. Update the existing
actions/checkout@v4 step to disable credential persistence by setting
persist-credentials to false so the workflow no longer leaves the token in git
config.

Source: Linters/SAST tools

Comment on lines +62 to +67
- uses: subosito/flutter-action@v2
with:
flutter-version: "3.44.5"

- run: flutter pub get
- run: flutter build apk --release --flavor production

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

What is the latest stable Flutter SDK version as of July 2026?

💡 Result:

As of July 9, 2026, the latest stable version of the Flutter SDK is 3.44.5 [1][2]. This version is part of the Flutter 3.44 release series, which was officially released in May 2026 [3]. You can ensure you are on the latest stable version by running the following commands in your terminal [4][2]: $ flutter channel stable $ flutter upgrade

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the workflow around the referenced lines
FILE=".github/workflows/build-tc-helper.yml"
wc -l "$FILE"
sed -n '1,140p' "$FILE" | nl -ba | sed -n '45,90p'

Repository: CCExtractor/taskwarrior-flutter

Length of output: 250


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/build-tc-helper.yml"

# Show the relevant section with line numbers using tools likely available in the sandbox
awk 'NR>=45 && NR<=75 { printf "%4d  %s\n", NR, $0 }' "$FILE"

Repository: CCExtractor/taskwarrior-flutter

Length of output: 1281


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/build-tc-helper.yml"

# Show the job header and early lines for timeout settings
awk 'NR>=1 && NR<=45 { printf "%4d  %s\n", NR, $0 }' "$FILE"

Repository: CCExtractor/taskwarrior-flutter

Length of output: 2111


Add an explicit timeout to the build job. The workflow has no timeout-minutes, so a hung Rust/Flutter build can burn runner minutes for hours; timeout-minutes: 45 is a reasonable guard.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-tc-helper.yml around lines 62 - 67, The build job in
the workflow needs an explicit timeout guard to prevent runaway Rust/Flutter
builds. Update the job that contains the flutter-action step and the flutter
build apk command to set a reasonable `timeout-minutes` value, using the
existing build job definition as the place to add it. Keep the change focused on
the workflow job configuration so the build is automatically terminated if it
hangs.

The search box only fed the local-taskc list (searchedTasks); the replica
view (TaskReplicaViewBuilder) read tasksFromReplica directly and ignored the
query, so typing in search did nothing in TaskChampion mode.

Add a reactive searchQuery (kept in sync by search()/toggleSearch()) and
filter the replica snapshot by task.description in home_page_body's Obx, so
the list narrows on each keystroke and restores when the query is cleared.
Matching is case-insensitive substring, consistent with the taskc path.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
lib/app/modules/home/controllers/home_controller.dart (1)

94-99: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant filter initialization — _profileSet() already sets these values.

_profileSet() is called at line 88 and already initializes pendingFilter.value (line 237) and waitingFilter.value (lines 238–243) from the same Query(storage.tabs.tab()) source. Lines 98–99 repeat the same assignments, so the comment "Without this the RxBool defaults to false" is misleading — the values are already set before these lines execute.

Consider removing the duplicate assignment or moving the comment to _profileSet() if the intent was to document that method's initialization.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/app/modules/home/controllers/home_controller.dart` around lines 94 - 99,
The pending/waiting filter setup in home_controller is duplicated because
_profileSet() already assigns both pendingFilter.value and waitingFilter.value
from Query(storage.tabs.tab()). Remove the repeated initialization here, or if
the note is meant to document first-load behavior, move the comment and any
related explanation to _profileSet() where the values are actually initialized.
Keep the behavior centered around _profileSet() and avoid maintaining the same
Query(storage.tabs.tab()) assignments in two places.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@lib/app/modules/home/controllers/home_controller.dart`:
- Around line 94-99: The pending/waiting filter setup in home_controller is
duplicated because _profileSet() already assigns both pendingFilter.value and
waitingFilter.value from Query(storage.tabs.tab()). Remove the repeated
initialization here, or if the note is meant to document first-load behavior,
move the comment and any related explanation to _profileSet() where the values
are actually initialized. Keep the behavior centered around _profileSet() and
avoid maintaining the same Query(storage.tabs.tab()) assignments in two places.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3c559344-a82f-4c29-8f14-f562153f6964

📥 Commits

Reviewing files that changed from the base of the PR and between d8d7fb0 and 0534000.

📒 Files selected for processing (2)
  • lib/app/modules/home/controllers/home_controller.dart
  • lib/app/modules/home/views/home_page_body.dart
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/app/modules/home/views/home_page_body.dart

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.

2 participants