Android: time range sheet, All shards screen, shards in the stream #285

Closed
opened 2026-10-17 08:24:09 +02:00 by agentic-developer · 1 comment
Collaborator

Part of #277. Needs #284.

Time range sheet All shards
04-time-range-sheet.png 06-all-shards.png

As in #284, the screenshots give content and behaviour, drawn in Material 3 Expressive.

Goal

The time range sheet behind the label of the range pill, the All shards screen behind "Show all N shards", and "Show shards in stream" of the ⋮ menu.

Plan

Time range sheet (ui/ActivityRange.kt; 04)

  1. A ModalBottomSheet titled "Time range" with Apply. A tap on the scrim cancels.
  2. Range input: the pending range in the text syntax (2026-10-03..2026-10-17), parsed as it is typed through activityRange (decided in #277: typed input is parsed). Below it are the resolved dates 2026-10-03 → 2026-10-17 and "15 days · to today", or the error of the core. The line of examples gets a label ("e.g."), and each example is a chip that fills the field.
  3. Tabs All · Days · Month · Year (PrimaryTabRow; All first, no Quarter, no Range tab):
    • All: "Since 6 Jan 2025 · N days" and Select all time.
    • Days: a month calendar with ‹ ›. One tap is a single day, a second tap a range (in either order). The range is a tinted band with dark end caps, future days are disabled, and a hint below explains the two taps.
    • Month: the year with ‹ › and 12 month buttons, future months disabled.
    • Year: a button per year with data.
  4. Quick picks as 44 dp chips, from activityQuickPicks: Today · Yesterday · Last 7 days · Last 30 days · This month · Last month · This year · Last year · All time.
  5. Whole days only. Apply sets the range on the screen of #284 and clears its back stack.

All shards (ui/ActivityShards.kt; 06)

  1. A destination of its own (without the bottom bar, like the note). Its header has back, the count and the range. Filter chips: All and one per series, each with its count. The list is grouped by day, newest first. Each row has the time, title, detail and series chip, and opens the note. It is paged as it scrolls (offset / limit of activity, a page of 50; open question 6 of the spec).

Show shards in stream

  1. The Stream screen takes an optional filter: "the files of these shards". It shows a dismissable chip Sport · Oct 2026 ✕ above the cards and only the cards of files holding a matching shard. The filter is a plain list of file ids from the report; the Stream keeps no knowledge of activities.

Tests

Pure: the two-tap range of the Days tab (either order, the same day twice, a future day refused), the months and years offered, the grouping by day, the page loading. Screenshot tests: the sheet on each tab (@TallScreenshots if needed), the All shards screen with a filter, and the Stream with the filter chip.

Docs

R35 (the app), README, AGENTS.md.

Part of #277. Needs #284. | Time range sheet | All shards | |---|---| | <img src="https://git.konstantinfickel.de/attachments/6b7c128e-2146-4127-b44a-8fcbbd11f812" width="260"> | <img src="https://git.konstantinfickel.de/attachments/6967452b-8ba4-4644-8a11-5b5cd77a7b28" width="260"> | | `04-time-range-sheet.png` | `06-all-shards.png` | As in #284, the screenshots give content and behaviour, drawn in Material 3 Expressive. ## Goal The time range sheet behind the label of the range pill, the All shards screen behind "Show all N shards", and "Show shards in stream" of the ⋮ menu. ## Plan ### Time range sheet (`ui/ActivityRange.kt`; `04`) 1. A `ModalBottomSheet` titled "Time range" with **Apply**. A tap on the scrim cancels. 2. **Range input**: the pending range in the text syntax (`2026-10-03..2026-10-17`), **parsed as it is typed** through `activityRange` (decided in #277: typed input is parsed). Below it are the resolved dates `2026-10-03 → 2026-10-17` and "15 days · to today", or the error of the core. The line of examples gets a label ("e.g."), and each example is a chip that fills the field. 3. **Tabs** All · Days · Month · Year (`PrimaryTabRow`; All first, no Quarter, no Range tab): - *All*: "Since 6 Jan 2025 · N days" and **Select all time**. - *Days*: a month calendar with ‹ ›. **One tap is a single day, a second tap a range** (in either order). The range is a tinted band with dark end caps, future days are disabled, and a hint below explains the two taps. - *Month*: the year with ‹ › and 12 month buttons, future months disabled. - *Year*: a button per year with data. 4. **Quick picks** as 44 dp chips, from `activityQuickPicks`: Today · Yesterday · Last 7 days · Last 30 days · This month · Last month · This year · Last year · All time. 5. Whole days only. **Apply** sets the range on the screen of #284 and clears its back stack. ### All shards (`ui/ActivityShards.kt`; `06`) 6. A destination of its own (without the bottom bar, like the note). Its header has back, the count and the range. Filter chips: All and one per series, each with its count. The list is grouped by day, newest first. Each row has the time, title, detail and series chip, and opens the note. It is **paged** as it scrolls (`offset` / `limit` of `activity`, a page of 50; open question 6 of the spec). ### Show shards in stream 7. The Stream screen takes an optional filter: "the files of these shards". It shows a dismissable chip `Sport · Oct 2026 ✕` above the cards and only the cards of files holding a matching shard. The filter is a plain list of file ids from the report; the Stream keeps no knowledge of activities. ## Tests Pure: the two-tap range of the Days tab (either order, the same day twice, a future day refused), the months and years offered, the grouping by day, the page loading. Screenshot tests: the sheet on each tab (`@TallScreenshots` if needed), the All shards screen with a filter, and the Stream with the filter chip. ## Docs R35 (the app), README, AGENTS.md.
Author
Collaborator

Implementation plan

Build on #284 / PR #309 and the behavior in this issue's description. #284 must be merged before implementation branches from main. At refinement time #284 remains open. Use Material 3 Expressive, existing app components and the prototype's content/behavior.

Decisions and clarified defaults

Clarification was requested about filter persistence and data-access strategy. No answer was received before recording this plan, so these are explicit implementation defaults:

  • The Stream filter persists across tab switches until its chip is dismissed; clear it when the repository/notes session changes.
  • Add small core/FFI selection helpers: filter by series before paging, and obtain complete matching file IDs without reading every summary. Filtering only a loaded page is insufficient; draining every report page merely to find file IDs is unnecessary.
  • Exclude mixed days remains a statistics presentation choice. It does not remove matching shards from All shards or the Stream filter. Series chip counts use inclusive shard counts.
  • A Stream filter is the file-ID snapshot selected when the action runs, as specified by the issue. Card contents refresh normally, removed files disappear, and reapplying the action recomputes the IDs. Existing text search intersects this file set.
  • #286 remains the activity-command editor. Timeline collision stacking remains a separate follow-up.

1. Core selection and bindings, tested first

Current api::activity pages computed.matches before reading summaries. Its page has no series selector, and the generic activityRange(text, today) resolves all as today alone. Address those gaps through the core:

  1. Share the existing match selection and summary conversion rather than duplicating query evaluation, outermost-match semantics, timezone handling or sorting.
  2. Add an activity-shards facade operation taking command key, parsed range, optional series index, now and PageRequest. Select that series before paging; return the existing Page shape with offset and the filtered total. Each shared shard occurs once in All and once in each matching series' result. Preserve the existing newest-first, file/line tie ordering.
  3. Add a lightweight full-range activity-files operation returning distinct matching file IDs from the same selection, with no store/summary reads and no page limit. The Stream action uses all series. Preserve the core's tracked-day bounds.
  4. Add a start-aware range resolver using RangeSpec::parse/resolve and the command's known tracking start. Keep the existing two-argument resolver's behavior available; bind an additive start-aware operation so the picker can preview all accurately without loading a report per keystroke.
  5. Bind these operations in streamd-ffi as conversion only. Reuse existing shard/page/range records where possible. Define and map the error for an invalid series selection; update Kotlin's exhaustive StreamdException.reason() mapping if a variant is added.
  6. Tests on MemoryStore: a selected series first appearing beyond the first 50 global matches, filtered offset/total, shared shards without duplicate All rows, empty/past-end pages, invalid selection, stable ordering, and summary reads confined to the requested page. A store that refuses reads verifies file-ID collection needs none. Test full-range unique IDs beyond the first page, tracking boundaries, and start-aware all resolution, including no tracking start.
  7. Cover new FFI conversions and a Kotlin-side smoke test. Preserve the existing report behavior used by #284 and the CLI.

Exact helper names can follow local conventions; the contracts above are the required behavior.

2. App adapters and immutable UI data

Extend core/Activity.kt, StreamSession and StreamViewModel; StreamSession remains the app's only binding caller:

  • Expose range validation/resolution, quick picks, selected shard pages and matching file IDs through Activities (or focused interfaces beside it). Supply project-local today and the report's start explicitly.
  • Extend the plain ActivityRange with the core's canonical range text and unit. #284 currently discards those fields; the sheet and routes need them.
  • Preserve summary.preview as the row's detail in ActivityShard, alongside its title/tag ranges, local date/time, file/line and all matching series.
  • Use plain success/failure/loading results, and keep binding work on the session dispatcher. Capture the stream snapshot and now for each paging generation; load subsequent pages against that generation. On a stream/configuration change, cancel/reset the generation, rather than appending pages from different snapshots.
  • Route parameters and saved state carry small values (key, canonical range text, selected series); summary pages and potentially large file-ID sets stay in the repository/session owner and are reloaded when necessary.

3. Range sheet and pending state

Add ui/ActivityRange.kt with an effect-owning sheet wrapper and internal content composable; pure selection helpers belong in a separate helper file:

  1. Tapping the pill label opens a ModalBottomSheet titled Time range with Apply. Initialize pending text from the current canonical range, including a drilled-in range. Preserve that text/tab/viewport while the sheet is open.
  2. Input, example chips, quick picks and calendar/month/year picks all update one pending selection. Resolve typed text through the core adapter as it changes; show resolved dates and inclusive day count, append “to today” when appropriate, or show the core error. Ignore stale validation responses. Disable Apply while invalid or validation is pending.
  3. Do not overwrite partially typed text with canonical text during validation. Apply accepts the core's canonical text, closes the sheet and starts a new ActivityPath root, clearing drill history. Scrim, Back and dismiss discard pending changes and leave the displayed range/history/mixed override untouched.
  4. Tabs: All, Days, Month, Year, in that order. All shows the known tracked span and Select all time; with no tracked start show an explicit empty-tracking presentation rather than an invented historical date.
  5. Days: Monday-first calendar, month navigation, whole-day picks. First tap selects a day; second tap selects an ordered inclusive range, in either direction. Same date twice is a single day; the next tap starts a new selection. A future tap is ignored without altering pending state. Draw the band and end caps and include the explanatory hint.
  6. Month: year navigation and 12 month buttons, future months disabled. Year: years from the tracking start through today, with an empty-data fallback based on today. Browsing a picker viewport does not itself apply a range. Returning to another tab keeps the shared pending value.
  7. Quick picks come from the core; use 44 dp chips and labelled example chips. Typed syntax retains the core's validity rules; do not silently clamp an accepted month/year/custom range to today. Future disabling belongs to picker actions.
  8. The sheet gets a fixed today/start snapshot for its editing session; recompute its context on reopening or safely restart if the repository changes. Strings owned by the app go into strings.xml; core labels/errors stay as returned.

4. All shards destination and paging

Add ui/ActivityShards.kt and an ActivityShardsRequest route, following NoteRequest's encoding conventions:

  • Open Show all N shards when total > 5 in every Activity view, including Day. Carry the current canonical range so a relative range does not shift under an open list.
  • Hide the bottom bar and Capture floating action button on this destination. Back returns to the exact Activity screen/drill state. Opening a note and returning retains the list's selected series and scroll position.
  • Header: count and range, back. Chips: All and every series, with full-range counts from the report, not loaded-row counts or exclusive statistics. Changing chips cancels in-flight loads and starts the selected series at offset zero.
  • Fetch pages of 50 through the filtered core operation. Initial loading, append loading, empty state, failed first page and append retry are explicit. One in-flight request per generation; retry uses the same offset; end is determined by the returned total. Advance by consumed raw page items, never the number of date headers or rendered groups.
  • Use a LazyColumn with stable (file, start-line) row keys and distinct day-header keys. Merge pages with no repeated day header at a page boundary and no duplicate shared shard. Rows preserve core ordering, show local time/title/detail and every matching series chip, and open the note at its line.
  • Stream/config updates reset page data and reconcile the selected series; stale responses cannot append after a chip/range/repository switch. Handle a command or series removed by a pull with an explicit result instead of an invalid request loop.

5. Show shards in Stream

Enable the Activity overflow action and extend ActivityActions/Shell navigation:

  • Resolve the full selected range's file IDs through the metadata-only core operation. While loading, prevent duplicate actions; on failure show an error and retain the previous Stream filter. A zero-match result is an active empty filter, not “no filter”.
  • On success, publish a generic StreamFilter(label, fileIds) and open the Stream tab using its normal tab navigation. Label example: “Sport · Oct 2026”.
  • StreamContent receives only the generic file-ID set and label; StreamActions gains dismissal. Stream knows nothing about activity keys, queries or paging.
  • Filter cards before day grouping/search/fast-scroller row calculation. Text search intersects filtered cards. Keep reminders, working-time banners and unreadable-file notices under their existing rules; the file filter concerns note cards.
  • Show a dismissible chip above the cards. Dismissal restores the full stream without changing the search text. A later activity action replaces the filter atomically; switching repositories clears it. Preserve the filter owner across ordinary tab navigation; reconstruct from a small descriptor after process restoration rather than saving a large ID list in a Bundle.

6. Tests and acceptance

Use London-style TDD in small signed conventional commits: interface fakes for sheet/pager/navigation contracts, core MemoryStore tests for selection, then screenshot scenes.

  • Pure picker tests: first/second/third taps, reverse order, same day, future refusal without mutation, month/year availability, leap/month/year boundaries, and no tracked start.
  • Wrapper/state tests: invalid input disables Apply, stale validation ignored, cancellation preserves current root, Apply resets history, all preview uses tracking start, and quick picks share core semantics.
  • Paging tests: more than 100 shards, a sparse series whose first match lies after global page 50, shared matches, pages splitting a day, empty and last partial pages, append failure/retry, rapid chip changes, stream generation reset and return from a note.
  • Stream tests: complete file IDs beyond preview/page one, duplicate IDs, active empty filter, search intersection, dismiss/replace/tab persistence/repository reset, and filtered fast-scroller/header consistency.
  • Screenshot tests using Fixtures TODAY/NOW/ZONE: sheet on all four tabs, invalid text, a selected day range, All shards with a series filter and details, empty/error/append states, and Stream with the filter chip. Add internal content composables with injected state; layoutlib effects do not run. Review every new/changed light/dark image and validate exact references.
  • Emulator acceptance: sheet Apply/cancel/Back, drilling then selecting a new root, all-shards scrolling/chips/note-and-back, Stream filter from a range larger than one page, dismissal and tab persistence. Record an unavailable emulator check explicitly.

7. Documentation and delivery

Update README.md, REQUIREMENTS.md R35 (and R36 API contracts where applicable), AGENTS.md and relevant docs: the new core/FFI methods, adapter records, routes, sheet/pager behavior and file-filter ownership. Replace #284's deferred/no-op descriptions with the implemented actions. #286 stays separate.

Run targeted Rust/core/FFI tests, the Kotlin binding check, app JVM/screenshot validation and the full nix build .#streamd-app. Finish with nix flake check. No new Maven dependency is expected; regenerate android/deps.json if dependencies/plugins change.

Follow the implement workflow for the branch from latest main after #284 merges, focused signed commits through nix develop, push, Forgejo PR, and the implementation findings/time/token-availability note.

## Implementation plan Build on #284 / PR #309 and the behavior in this issue's description. **#284 must be merged before implementation branches from main.** At refinement time #284 remains open. Use Material 3 Expressive, existing app components and the prototype's content/behavior. ### Decisions and clarified defaults Clarification was requested about filter persistence and data-access strategy. No answer was received before recording this plan, so these are explicit implementation defaults: - The Stream filter persists across tab switches until its chip is dismissed; clear it when the repository/notes session changes. - Add small core/FFI selection helpers: filter by series **before** paging, and obtain complete matching file IDs without reading every summary. Filtering only a loaded page is insufficient; draining every report page merely to find file IDs is unnecessary. - Exclude mixed days remains a statistics presentation choice. It does not remove matching shards from All shards or the Stream filter. Series chip counts use inclusive shard counts. - A Stream filter is the file-ID snapshot selected when the action runs, as specified by the issue. Card contents refresh normally, removed files disappear, and reapplying the action recomputes the IDs. Existing text search intersects this file set. - #286 remains the activity-command editor. Timeline collision stacking remains a separate follow-up. ### 1. Core selection and bindings, tested first Current api::activity pages computed.matches before reading summaries. Its page has no series selector, and the generic activityRange(text, today) resolves all as today alone. Address those gaps through the core: 1. Share the existing match selection and summary conversion rather than duplicating query evaluation, outermost-match semantics, timezone handling or sorting. 2. Add an activity-shards facade operation taking command key, parsed range, optional series index, now and PageRequest. Select that series before paging; return the existing Page<ActivityShard> shape with offset and the **filtered** total. Each shared shard occurs once in All and once in each matching series' result. Preserve the existing newest-first, file/line tie ordering. 3. Add a lightweight full-range activity-files operation returning distinct matching file IDs from the same selection, with no store/summary reads and no page limit. The Stream action uses all series. Preserve the core's tracked-day bounds. 4. Add a start-aware range resolver using RangeSpec::parse/resolve and the command's known tracking start. Keep the existing two-argument resolver's behavior available; bind an additive start-aware operation so the picker can preview all accurately without loading a report per keystroke. 5. Bind these operations in streamd-ffi as conversion only. Reuse existing shard/page/range records where possible. Define and map the error for an invalid series selection; update Kotlin's exhaustive StreamdException.reason() mapping if a variant is added. 6. Tests on MemoryStore: a selected series first appearing beyond the first 50 global matches, filtered offset/total, shared shards without duplicate All rows, empty/past-end pages, invalid selection, stable ordering, and summary reads confined to the requested page. A store that refuses reads verifies file-ID collection needs none. Test full-range unique IDs beyond the first page, tracking boundaries, and start-aware all resolution, including no tracking start. 7. Cover new FFI conversions and a Kotlin-side smoke test. Preserve the existing report behavior used by #284 and the CLI. Exact helper names can follow local conventions; the contracts above are the required behavior. ### 2. App adapters and immutable UI data Extend core/Activity.kt, StreamSession and StreamViewModel; StreamSession remains the app's only binding caller: - Expose range validation/resolution, quick picks, selected shard pages and matching file IDs through Activities (or focused interfaces beside it). Supply project-local today and the report's start explicitly. - Extend the plain ActivityRange with the core's canonical range text and unit. #284 currently discards those fields; the sheet and routes need them. - Preserve summary.preview as the row's detail in ActivityShard, alongside its title/tag ranges, local date/time, file/line and all matching series. - Use plain success/failure/loading results, and keep binding work on the session dispatcher. Capture the stream snapshot and now for each paging generation; load subsequent pages against that generation. On a stream/configuration change, cancel/reset the generation, rather than appending pages from different snapshots. - Route parameters and saved state carry small values (key, canonical range text, selected series); summary pages and potentially large file-ID sets stay in the repository/session owner and are reloaded when necessary. ### 3. Range sheet and pending state Add ui/ActivityRange.kt with an effect-owning sheet wrapper and internal content composable; pure selection helpers belong in a separate helper file: 1. Tapping the pill label opens a ModalBottomSheet titled Time range with Apply. Initialize pending text from the current canonical range, including a drilled-in range. Preserve that text/tab/viewport while the sheet is open. 2. Input, example chips, quick picks and calendar/month/year picks all update one pending selection. Resolve typed text through the core adapter as it changes; show resolved dates and inclusive day count, append “to today” when appropriate, or show the core error. Ignore stale validation responses. Disable Apply while invalid or validation is pending. 3. Do not overwrite partially typed text with canonical text during validation. Apply accepts the core's canonical text, closes the sheet and starts a new ActivityPath root, clearing drill history. Scrim, Back and dismiss discard pending changes and leave the displayed range/history/mixed override untouched. 4. Tabs: All, Days, Month, Year, in that order. All shows the known tracked span and Select all time; with no tracked start show an explicit empty-tracking presentation rather than an invented historical date. 5. Days: Monday-first calendar, month navigation, whole-day picks. First tap selects a day; second tap selects an ordered inclusive range, in either direction. Same date twice is a single day; the next tap starts a new selection. A future tap is ignored without altering pending state. Draw the band and end caps and include the explanatory hint. 6. Month: year navigation and 12 month buttons, future months disabled. Year: years from the tracking start through today, with an empty-data fallback based on today. Browsing a picker viewport does not itself apply a range. Returning to another tab keeps the shared pending value. 7. Quick picks come from the core; use 44 dp chips and labelled example chips. Typed syntax retains the core's validity rules; do not silently clamp an accepted month/year/custom range to today. Future disabling belongs to picker actions. 8. The sheet gets a fixed today/start snapshot for its editing session; recompute its context on reopening or safely restart if the repository changes. Strings owned by the app go into strings.xml; core labels/errors stay as returned. ### 4. All shards destination and paging Add ui/ActivityShards.kt and an ActivityShardsRequest route, following NoteRequest's encoding conventions: - Open Show all N shards when total > 5 in every Activity view, including Day. Carry the current canonical range so a relative range does not shift under an open list. - Hide the bottom bar and Capture floating action button on this destination. Back returns to the exact Activity screen/drill state. Opening a note and returning retains the list's selected series and scroll position. - Header: count and range, back. Chips: All and every series, with full-range counts from the report, not loaded-row counts or exclusive statistics. Changing chips cancels in-flight loads and starts the selected series at offset zero. - Fetch pages of 50 through the filtered core operation. Initial loading, append loading, empty state, failed first page and append retry are explicit. One in-flight request per generation; retry uses the same offset; end is determined by the returned total. Advance by consumed raw page items, never the number of date headers or rendered groups. - Use a LazyColumn with stable (file, start-line) row keys and distinct day-header keys. Merge pages with no repeated day header at a page boundary and no duplicate shared shard. Rows preserve core ordering, show local time/title/detail and every matching series chip, and open the note at its line. - Stream/config updates reset page data and reconcile the selected series; stale responses cannot append after a chip/range/repository switch. Handle a command or series removed by a pull with an explicit result instead of an invalid request loop. ### 5. Show shards in Stream Enable the Activity overflow action and extend ActivityActions/Shell navigation: - Resolve the full selected range's file IDs through the metadata-only core operation. While loading, prevent duplicate actions; on failure show an error and retain the previous Stream filter. A zero-match result is an active empty filter, not “no filter”. - On success, publish a generic StreamFilter(label, fileIds) and open the Stream tab using its normal tab navigation. Label example: “Sport · Oct 2026”. - StreamContent receives only the generic file-ID set and label; StreamActions gains dismissal. Stream knows nothing about activity keys, queries or paging. - Filter cards before day grouping/search/fast-scroller row calculation. Text search intersects filtered cards. Keep reminders, working-time banners and unreadable-file notices under their existing rules; the file filter concerns note cards. - Show a dismissible chip above the cards. Dismissal restores the full stream without changing the search text. A later activity action replaces the filter atomically; switching repositories clears it. Preserve the filter owner across ordinary tab navigation; reconstruct from a small descriptor after process restoration rather than saving a large ID list in a Bundle. ### 6. Tests and acceptance Use London-style TDD in small signed conventional commits: interface fakes for sheet/pager/navigation contracts, core MemoryStore tests for selection, then screenshot scenes. - Pure picker tests: first/second/third taps, reverse order, same day, future refusal without mutation, month/year availability, leap/month/year boundaries, and no tracked start. - Wrapper/state tests: invalid input disables Apply, stale validation ignored, cancellation preserves current root, Apply resets history, all preview uses tracking start, and quick picks share core semantics. - Paging tests: more than 100 shards, a sparse series whose first match lies after global page 50, shared matches, pages splitting a day, empty and last partial pages, append failure/retry, rapid chip changes, stream generation reset and return from a note. - Stream tests: complete file IDs beyond preview/page one, duplicate IDs, active empty filter, search intersection, dismiss/replace/tab persistence/repository reset, and filtered fast-scroller/header consistency. - Screenshot tests using Fixtures TODAY/NOW/ZONE: sheet on all four tabs, invalid text, a selected day range, All shards with a series filter and details, empty/error/append states, and Stream with the filter chip. Add internal content composables with injected state; layoutlib effects do not run. Review every new/changed light/dark image and validate exact references. - Emulator acceptance: sheet Apply/cancel/Back, drilling then selecting a new root, all-shards scrolling/chips/note-and-back, Stream filter from a range larger than one page, dismissal and tab persistence. Record an unavailable emulator check explicitly. ### 7. Documentation and delivery Update README.md, REQUIREMENTS.md R35 (and R36 API contracts where applicable), AGENTS.md and relevant docs: the new core/FFI methods, adapter records, routes, sheet/pager behavior and file-filter ownership. Replace #284's deferred/no-op descriptions with the implemented actions. #286 stays separate. Run targeted Rust/core/FFI tests, the Kotlin binding check, app JVM/screenshot validation and the full nix build .#streamd-app. Finish with nix flake check. No new Maven dependency is expected; regenerate android/deps.json if dependencies/plugins change. Follow the implement workflow for the branch from latest main after #284 merges, focused signed commits through nix develop, push, Forgejo PR, and the implementation findings/time/token-availability note.
Sign in to join this conversation.
No labels
android
cli
epic
planned
tui
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
kfickel/streamd#285
No description provided.