Add Command to mark reminders as done #274
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Add
appendto file commandadd
streamd <file-command> append (content of the line)that adds a shard on top-level to the end of the file the corresponding file-command would have opened / created.The line should be a H1-heading
#, with if added to an existing file with a leading line before and a date + time marker (so that it's marked as "now") if the shard would have a different value otherwise (can only become time value if date is already correct).Change Remind Commands to have a fixed name
Instead of a rule list, it should be a command
Add
streamd <remind command> doneAndroid App
Reminders in the Config or top of the stream should have a button "Done" on the buttom right that uses the logic from
streamd <remind command> doneto mark the reminder as done.The text of the reminder should not be mentioned twice, that doesn't look too great.
Add Command to mark remto Add Command to mark reminders as doneImplementation plan
Decisions (from refinement)
[[commands.<key>.rules]]is removed. Each reminder is its own[commands.<key>]withtype = "reminder". The rule's fields move up into the command:description,start,trigger,expect, and the new optionalinto_file. There is nonamefield: thedescriptionis the reminder's text (Go, call @XYZ: due since 2026-10-11 09:00 (never)), and the key is used when it is missing. An oldrulesfield is rejected with a message that says how to rewrite it: one[commands.<key>]per rule.# @CalledXYZ @143000. A date marker is added only when it is needed (see below).donesupports onlydaysandsincereminders. Forifreminders (which are due per past day),donefails, and the app shows no Done button.append/donehas to create the target file, the file holds only the line, not the template. The file name already gives the moment, so no markers are added.into_fileis optional. Without it,streamd <key> donefails with a clear message, and the app shows no Done button.1. Core:
appendon file commands (commands/file/)New in
commands/file/target.rs(or a newappend.rs), on aStreamStore:pub fn append(store, shards, command: &FileCommand, line_body: &str, now, tz, config) -> Result<Appended, CoreError>:targetdoes for the current span: the existing match of the span, else a file to create. Reusefile_target, but do not create the template file.Appended::Existing { id }): read it, add\nif it does not end in one, add a blank line, then# <markers> <body>\n. Write it.Appended::Created { id }):create_newwith the content# <body>\nalone (no template, no markers), named asfile_name(command, now).fn moment_markers(file_moment, now, tz) -> Vec<String>), pure: the shard must read as now. If the local date of the file's moment equals today, only@HHMMSSis added. Otherwise@YYYYMMDD @HHMMSSis added. A created file needs none. Unit-test this next tolocal_to_utcedge cases (DST).api.rs:append_to_file(project, stream, store, key, body, now) -> Appended. AddAppendedtostreamd-ffi(records.rs) and as aProjectmethod.CLI (
commands/file/cli.rs/run.rs):streamd <file-key> append <TEXT...>(the words are joined by spaces). It cannot be combined with a date,editor-i(usage error, exit 2). It printsAppended to <path>orCreated <path>(paths as in R21). It does not open an editor.<TEXT>is written as given; leading tags in it become markers, sostreamd daily append @Task call Bobworks.2. Core: reminder configuration (
config/project/)file.rs:ReminderCommandFile { description, expect, trigger, start, into_file }, withdeny_unknown_fields.ReminderRuleFileis removed.rulesbecomes a dedicated error (reminders have no rules anymore; write one [commands.<key>] with type = "reminder" per rule). Hook it into the hand-written command reading so the error keeps its line.validate.rs→ runtimeReminderCommand { description: Option<String>, expect, trigger, start, into_file: Option<String> }. TheReminderRulestruct is removed, and itsexpectsmoves onto the command. Validation is as in R29 today, atcommands.<key>.expect…/.trigger…. In addition:into_fileMUST name a configured file command (typo suggestion viaclosest).into_file, the trigger MUST NOT beif(`done` cannot mark a day in the past; remove into_file or use days/since).into_file, the done tags MUST be derivable (see §3). Otherwise:no tag places { remind = "CalledXYZ" } on its own, so done cannot write it; …(listing and suggestions as in R31 rule 2).due.rs:due_remindersworks on one command, andDueReminder.rulebecomes the command's display text (descriptionor key).stream_reminderskeeps the(key, DueReminder)pairs.summary()is unchanged.rules:schema.rs(fields ofConfigTable::Command(Reminder)),complete.rs(values offered inexpect/ifare now at the command's top level, plusinto_filecompleting file command keys),index.rs(the occurrences inexpect/if, andinto_fileas a reference to a command sorename_commandupdates it),rename.rs(rename_commandalso rewritesinto_file),outline.rsandchange.rs(a reminder command has the rule's fields directly; a field added to the file model needs its line inchange.rs), andlocate.rs.init.toml: replace the commented[commands.remind]+ rules with commented examples of two reminder commands, one withinto_file = "daily". Mentionstreamd <key> done.3. Core:
donecommands/reminder/done.rs:pub fn done_tags(command: &ReminderCommand, file: &FileCommand, config) -> Option<Vec<String>>, pure. For each alternative ofexpectin order: remove the entries that the file command'splacementalready gives (its files carry them). Then find one configured tag whose unconditional rules (nowith), applied alone, place every remaining entry. The approach is analogous tofile_types_placing: runplacement_ofa shard with only that marker. The first alternative that has such a tag wins. If several tags qualify, the canonical one that sorts first by name wins (deterministic). An empty remainder needs no tag, but the line is still written for its moment. Returns the tags written as@Name(never an alias, as derivations never write one).pub fn done(store, shards, project, key, now, tz) -> Result<Appended, CoreError>: it checks that the command is a reminder withinto_file, and notif. Then it callsappendof §1 with the body@Tag(+ markers frommoment_markers). NewCoreErrorvariants:NoIntoFile { key }andDoneNotSupported { key }(forif), each with a wording. Add them toStreamdExceptioninstreamd-ffi/error.rs.doneis allowed even when the reminder is not due (it just records it). It is idempotent in effect.api.rs:reminder_done(project, stream, store, key, now) -> Appended, andcan_mark_done(project, key) -> bool(into_file set and the trigger is notif) for the hosts. Bind both instreamd-ffi/project.rs. Adddone: Booleanto the due-reminder record (or exposecanMarkDone) and addkeyto the record so the app knows what to call.MemoryStore: appending to today's daily (only@HHMMSS), to an older match (date + time), to a created file (line only), an alias inexpect(writes the canonical tag),expectalready satisfied by the file placement, asincereminder that is no longer due afterwards (due_reminderson the reloaded stream is empty), and the errors.4. CLI
streamd <reminder-key>still prints its due line orNothing due.streamd <reminder-key> donecallsdonethroughFsStoreand printsMarked <description> as done in <path>. Withoutinto_file/ withif, it prints the core's error, exit 1.commands/frontend/views.rs,commands/reminder/interactive/): reminder commands are no longer tabs. One built-in viewremindis built next to the maintenance (likeconfig, apart from the others, but only if at least one reminder command exists). It lists the due reminders of all reminder commands, with the key barj/k move e edit d done r reload.drunsdoneon the selected reminder (if it cannot be marked done, the status line says why), then reloads.streamd <reminder-key> -iopens the front-end on this view with that reminder selected.config_actionsthat iteraterulesare adapted. Optionally offer a code action "Mark as done" — not in scope, skip.5. Android
core/StreamCard.kt:Remindergetskey: StringandcanMarkDone: Boolean. Thesummarystays andruleis dropped (it was the duplicated text).core/StreamSession.kt:reminderDone(key)→project.reminderDone(stream, store, key, now), thenchanged(id)of the appended/created file. It goes throughRepositoryConnection.written(id)in the view model, so a commit-every-save commits it.StreamViewModel/StreamReading:markDone(key). A failure is shown as a snackbar withreason().ui/Stream.kt(reminder banners) andui/AllCommands.kt(Reminders section): each reminder row shows the summary once (no second line with the rule name). WhencanMarkDone, a "Done" text button sits at the bottom right of the banner. Tapping the row still opens the file of anifreminder. Build this onBannerinComponents.kt(with an optional action slot) so both screens share it.ConfigCommandForms.ktReminderFields,ConfigList.ktruleText/commandText,ConfigDraft.newCommand): the reminder form editsdescription,expect,trigger,startdirectly (no rule list), plusinto_fileas aChoiceEntryof the file commands. FFIReminderSettingschanges accordingly (outline.rs, with its round-trip test).StreamScreenshots(reminders with and without Done),AllCommandsScreenshots,ConfigScreenshots/ConfigEditScreenshots(reminder form). Re-record and review every changed image.6. Documentation
REQUIREMENTS.md: rewrite R29 (one command per reminder, the fields, the validation ofinto_fileand the done tags,streamd <key> done, the shared view withd). Extend R31 withappend(the target, the line, the moment markers, the created file without template). Update R28's field list forreminder, R30 (the shared reminder view instead of one tab per command), and the app requirement for the Done button.README.md: the reminder examples,streamd <key> done,streamd <key> append.AGENTS.md: thecommands/reminder/description (done.rs, no rules),commands/file/(append), the front-end's built-in reminder view, the facade functions (append_to_file,reminder_done,can_mark_done), and the AndroidReminderfields /markDone.Order of work (each its own commit)
feat(file): append a line to the file of a file command(core + CLI + FFI)feat(reminder)!: one command per reminder(config model, validation, outline/change/index/complete/schema, init.toml, CLI, LSP, FFI records, app config forms). This is a breaking config change.feat(reminder): mark a reminder as done into a file command(coredone, CLIdone, shared TUI view withd)feat(android): mark reminders as done, show their text oncedocs: …Run
cargo test,nix flake checkandnix build .#streamd-app(screenshot validation) at the end.Implemented in #275.
Effort: about 36 minutes of wall-clock time by the session's clock, and roughly 375k tokens (an estimate from the session's token budget, not an exact count).
Findings during the implementation
donecan only write a reminder off if some tag places what it expects on its own. A reminder that expects only a file type (expect = { file_type = "daily" }) cannot be done into a file command, so validation rejects such aninto_fileinstead of failing later. The issue's example (expect = { remind = "CalledXYZ" }) needs a tag such as[tags.CalledXYZ]withdimension = "remind".[commands.remind.called_xyz]became[commands.called_xyz], as decided in refinement. Command keys may not contain_(the init examples aredaily-noteandweekly-review).donewrites only the moment markers.# @Task @143000 call Bob), so they always stay among the shard's markers.remind.tui::tab_line_apartnow takes a list of tabs apart.into_filewas validated.