Activity: bindings for Kotlin #283

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

Part of #277. Needs #279 (and #278 for the outline records).

Goal

The activity command crosses into Kotlin: the report of #279, the range syntax and the configuration records of #278. Conversion only, no logic (crates/streamd-ffi).

Plan

  1. project.activity(stream, key, range: String, excludeMixed: Boolean?, now, offset: UInt, limit: UInt) -> ActivityReport. range is the text syntax, so no RangeSpec record crosses back. Null excludeMixed means the command's own setting. The report carries both Days tables, and a host toggles without calling again.
  2. Records (records.rs): ActivityReport { range: ActivityRange { from, to, label, unit, canStepBack, canStepForward, previous: String?, next: String? }, shape: ActivityShape, series: List<ActivitySeries { name, color: String }>, days: List<ActivityDay { date, counts: List<UInt>, kind: DayKind (None, Single, Mixed, Future, Untracked), drawn: List<UInt> /* series indices to draw, 1 = single colour, 2 = halves, 3+ = wedges */, level: UByte }>, stats: ActivityStats, shards: ActivityShardPage { total, items: List<ActivityShard> }, shared: List<EntrySource> }. previous / next are the stepped ranges as text, so the app needs no date arithmetic for the arrows.
  3. activityRange(text, today) -> ActivityRange (the parse and the resolved dates, for the sheet's field, throwing StreamdException.InvalidRange with the wording of the core), and activityQuickPicks(today) -> List<QuickPick { label, range }>.
  4. outline.rs: CommandSettings.Activity with its series, both ways, plus the round trip test with an activity command added to the starter configuration.
  5. error.rs: the new CoreError variants.
  6. tests/kotlin/StoreTest.kt: the sample month of #279 through a MemoryStore-like map store.

Tests

Rust tests of the conversions (dates, the level, the drawn indices, paging), the outline round trip, the Kotlin test (nix build .#checks.x86_64-linux.ffi-kotlin).

Docs

R35 (what is bound), AGENTS.md (records.rs, outline.rs).

Part of #277. Needs #279 (and #278 for the outline records). ## Goal The activity command crosses into Kotlin: the report of #279, the range syntax and the configuration records of #278. Conversion only, no logic (`crates/streamd-ffi`). ## Plan 1. `project.activity(stream, key, range: String, excludeMixed: Boolean?, now, offset: UInt, limit: UInt) -> ActivityReport`. `range` is the text syntax, so no `RangeSpec` record crosses back. Null `excludeMixed` means the command's own setting. The report carries **both** Days tables, and a host toggles without calling again. 2. Records (`records.rs`): `ActivityReport { range: ActivityRange { from, to, label, unit, canStepBack, canStepForward, previous: String?, next: String? }, shape: ActivityShape, series: List<ActivitySeries { name, color: String }>, days: List<ActivityDay { date, counts: List<UInt>, kind: DayKind (None, Single, Mixed, Future, Untracked), drawn: List<UInt> /* series indices to draw, 1 = single colour, 2 = halves, 3+ = wedges */, level: UByte }>, stats: ActivityStats, shards: ActivityShardPage { total, items: List<ActivityShard> }, shared: List<EntrySource> }`. `previous` / `next` are the stepped ranges as text, so the app needs no date arithmetic for the arrows. 3. `activityRange(text, today) -> ActivityRange` (the parse and the resolved dates, for the sheet's field, throwing `StreamdException.InvalidRange` with the wording of the core), and `activityQuickPicks(today) -> List<QuickPick { label, range }>`. 4. `outline.rs`: `CommandSettings.Activity` with its series, both ways, plus the round trip test with an activity command added to the starter configuration. 5. `error.rs`: the new `CoreError` variants. 6. `tests/kotlin/StoreTest.kt`: the sample month of #279 through a `MemoryStore`-like map store. ## Tests Rust tests of the conversions (dates, the level, the drawn indices, paging), the outline round trip, the Kotlin test (`nix build .#checks.x86_64-linux.ffi-kotlin`). ## Docs R35 (what is bound), AGENTS.md (`records.rs`, `outline.rs`).
Author
Collaborator

Implemented in #307.

Time: about 60 minutes (including the app build with its screenshot tests and nix flake check).
Tokens: about 260k for the session, most of it reading the facade, the bindings and the app.

Findings

  • Step 2 of the plan needed a small change in the core: the core had can_step_back / can_step_forward but no stepped ranges. Rather than step the range inside the bindings (logic there), the facade got an ActivityRange (days, label, the range as written, and previous / next), used by the report and by a new resolve_activity_range(text, today). The bindings only turn the specs into text.
  • activity takes a store as its first argument, like the other calls that read files: the summaries of the page of shards are read from it.
  • excludeMixed only overrides the flag of the report. The stats always carry both ways to count a mixed day, each with its percentages (from Stats::percent), so the app needs no arithmetic.
  • DayKind is built from the core's status and classification. counts is empty on a day that is not tracked, as in the core.
  • Step 5 needed nothing: InvalidRange already crossed.
  • New Kotlin enum variants reach the app's exhaustive whens (CommandKind, CommandSettings). The app therefore got the minimum to compile: activity commands are listed in the configuration, their form shows the description only, and the "new command" menu leaves them out until #286.
  • A shard's summary title keeps its time markers (@Run @190000 Home again). The app already hides those with isMoment; #284 should do the same in its shard list.
  • With one tag per dimension, # @Run @Climbing places only one value. A shard that is "shared" between series needs overlapping queries, not two tags of the same dimension. The tests use that.
  • Fixed in passing: a doc comment of FileTarget in records.rs had a line of code pasted into it.
Implemented in #307. **Time:** about 60 minutes (including the app build with its screenshot tests and `nix flake check`). **Tokens:** about 260k for the session, most of it reading the facade, the bindings and the app. **Findings** - Step 2 of the plan needed a small change in the core: the core had `can_step_back` / `can_step_forward` but no stepped ranges. Rather than step the range inside the bindings (logic there), the facade got an `ActivityRange` (days, label, the range as written, and `previous` / `next`), used by the report and by a new `resolve_activity_range(text, today)`. The bindings only turn the specs into text. - `activity` takes a `store` as its first argument, like the other calls that read files: the summaries of the page of shards are read from it. - `excludeMixed` only overrides the flag of the report. The stats always carry both ways to count a mixed day, each with its percentages (from `Stats::percent`), so the app needs no arithmetic. - `DayKind` is built from the core's status and classification. `counts` is empty on a day that is not tracked, as in the core. - Step 5 needed nothing: `InvalidRange` already crossed. - New Kotlin enum variants reach the app's exhaustive `when`s (`CommandKind`, `CommandSettings`). The app therefore got the minimum to compile: activity commands are listed in the configuration, their form shows the description only, and the "new command" menu leaves them out until #286. - A shard's summary title keeps its time markers (`@Run @190000 Home again`). The app already hides those with `isMoment`; #284 should do the same in its shard list. - With one tag per dimension, `# @Run @Climbing` places only one value. A shard that is "shared" between series needs overlapping queries, not two tags of the same dimension. The tests use that. - Fixed in passing: a doc comment of `FileTarget` in `records.rs` had a line of code pasted into it.
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#283
No description provided.