Skip to content

Add reminder run command - #2239

Open
Aaronontheweb wants to merge 9 commits into
netclaw-dev:devfrom
Aaronontheweb:feat/reminder-run-now
Open

Aaronontheweb wants to merge 9 commits into
netclaw-dev:devfrom
Aaronontheweb:feat/reminder-run-now

Conversation

@Aaronontheweb

@Aaronontheweb Aaronontheweb commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds netclaw reminder run <id>. This command lets an operator run an
existing reminder now, so they can test it without a wait for its
schedule. It waits for the run to finish and prints the result.

  • POST /api/reminders/{id}/run uses the same Operator-only authority
    gate as reminder create (ResolveReminderAuthorizationContext /
    PrincipalClassification.Operator). A non-Operator caller gets a
    403.
  • The daemon runs the reminder's real prompt, to its real delivery
    target, through the same execution path a scheduled run uses.
  • The endpoint waits for the run to settle, then returns: the
    reminder id, source (manual), status (ok / failed /
    timed_out), start time, duration, session id, delivery target (or
    none), the session's final assistant reply text (trimmed to a
    sane length, with a flag when it trimmed), and the error message on
    failure.
  • The wait has a bound: the execution actor's own attempt timeout
    (ReminderExecutionActor.ExecutionAttemptTimeout, one hour) plus a
    settlement margin. If the run has not settled by then, the endpoint
    returns a timed_out result with the session id. It does not
    cancel the run.
  • The CLI prints a short result block (status, duration, session,
    delivery, reply or error) and the exact netclaw chat --resume <id>
    line to open the session. Exit code is 0 for ok, non-zero for
    failed or timed_out. --json prints the result object. The
    CLI's HTTP client timeout for this call exceeds the daemon's own
    wait.
  • Fixed a bug this work exposed: ReminderExecutionActor subscribed
    only to OutputFilter.TextStreaming. A provider that returns its
    whole reply in one chunk, with no incremental deltas, never filled
    the reply text. It now subscribes to both Text and
    TextStreaming; ExecutionOutputAccumulator's existing guard
    against double counting keeps a provider that streams both correct.
  • A manual run does not change the schedule. A one-shot reminder keeps
    its original fire time. An interval or cron reminder keeps its next
    fire time.
  • History records the source of each run, manual or scheduled.
    netclaw reminder history <id> and netclaw reminder status <id>
    show it in a new column.
  • A manual run failure does not count toward the scheduled
    consecutive-failure limit (auto-disable).
  • If the reminder already runs, the manual run fails with a clear
    error. It does not queue, and it does not reintroduce the global
    concurrency cap that fix(reminders): remove execution cap so reminders never skip on capacity #1839 removed.
  • Updated the netclaw-operations skill's scheduling reference: the
    Manual run section now covers the result block, how to read each
    field, the exit codes, and the timeout case. Added a subsection on
    testing a reminder inside a session with set_reminder and
    delivery_kind=current_session. Bumped the skill version twice on
    this branch (2.75.3 -> 2.76.0 -> 2.77.0).

Design choice to check

A manual run's settlement (SettleManualExecution in
ReminderManagerActor) logs success or failure but does not post a
channel failure notice the way a scheduled failure does. The operator
now gets the full result directly from the CLI, so a duplicate channel
post felt like noise for a run the operator asked for directly. Flag
if you want manual failures to also post to the reminder's channel.

Validation

  • dotnet build Netclaw.slnx -c Release: 0 errors, 0 new warnings
    (one pre-existing ASPIRE010 warning, unrelated to this change).
  • dotnet test src/Netclaw.Actors.Tests -c Release --filter "FullyQualifiedName~Reminder":
    210 passed, 0 failed.
  • dotnet test src/Netclaw.Daemon.Tests -c Release --filter "FullyQualifiedName~Reminder":
    20 passed, 0 failed.
  • dotnet test src/Netclaw.Cli.Tests -c Release --filter "FullyQualifiedName~Reminder":
    6 passed, 0 failed.
  • dotnet slopwatch analyze: 0 issues.
  • pwsh ./scripts/Add-FileHeaders.ps1 -Verify: all files have headers.
  • Manual end-to-end proof: isolated NETCLAW_HOME, HOME, and daemon
    port (57199); the smoke LLM server as the model. Ran an interval
    reminder with netclaw reminder run <id> and confirmed the printed
    result block: Status: ok, the real reply text ("Netclaw smoke
    response."), the session line, and exit 0. Ran it again with
    --json and confirmed the result object, including replyText.
    Pointed the main model at an unreachable provider (the daemon
    picked up the change through its normal config-watch restart),
    ran the same reminder again, and confirmed Status: failed, the
    connection-refused error message, and exit 1. Ran
    netclaw reminder run does-not-exist and confirmed a clear
    not-found message and exit 1. reminder history <id> showed all
    three manual runs with their correct status. All processes stopped
    after the run; the real ~/.netclaw and the default daemon port
    (5199) were never touched.

This PR adds no tests, at the maintainer's request.

Refs #1511. Replaces draft #1527 (stale — see that PR for the original
requirements; not modified here).

A reminder now supports a manual run, separate from its schedule.
RunReminderNowCommand asks for Operator authority and starts the
reminder now, outside its normal fire time.

Each execution now carries a source: scheduled or manual. History
records the source. A manual run has no Akka.Reminders envelope, so
the tracker and execution actor carry no envelope for it, and
settlement skips Ack/Nack, schedule changes, and the scheduled
consecutive-failure count.

A manual run rejects fast when the reminder is missing, disabled,
expired, already active, or scheduling is off. It does not queue.
POST /api/reminders/{id}/run starts a reminder now. It uses the same
Operator authority gate as reminder create. A non-Operator caller
gets a 403.

The endpoint maps manager rejections to matching HTTP codes: 404 for
a missing reminder, 409 when it already runs, and 400 for other
rejections such as disabled or expired.
netclaw reminder run <id> calls the new daemon endpoint and starts a
reminder now. An unknown id fails with a clear message and a
non-zero exit. A daemon that is not running fails with the same
error style as the other reminder subcommands.

reminder history and reminder status now show each run's source,
manual or scheduled, in a new column.

This commit also fixes a pre-existing format bug on the history
table's fired_at column: the alignment spec sat after the format
specifier, so it printed as part of the date format instead of
padding the column.
Add a Manual run section to the scheduling reference: the command,
what it does and does not change, and when the agent should (and
should not) offer it in conversation.

Bump the netclaw-operations skill version.
@Aaronontheweb Aaronontheweb added the tui Terminal UI (Termina) issues label Sep 24, 2026
The manager now sends an accept ack for a run request, then waits
for the run to settle before it replies again. The wait has a bound:
the execution timeout plus a settlement margin.

The execution actor now reports the final reply text to the manager.
The reminder channel now subscribes to both Text and TextStreaming
output. A provider that sends the whole reply in one chunk, with no
streaming deltas, used to leave the reply text empty.

A manual run keeps its prior guarantees: it does not touch the
schedule, and it does not count toward scheduled failure accounting.
POST /api/reminders/{id}/run now makes two calls to the manager: an
accept ack, then a wait for the run to settle. The response carries
the reminder id, status, start time, duration, session id, delivery
target, the reply text, and the error message on failure.

On timeout, the endpoint returns a clear "still running" result with
the session id. It does not cancel the run.

The existing rejections keep their exact codes and messages: not
Operator, scheduling disabled, not found, disabled, expired, and
already executing.
`netclaw reminder run <id>` now waits for the daemon and prints the
result: status, duration, session, delivery target, and the reply or
the error. It prints the `netclaw chat --resume <id>` line so the
operator can open the session. Exit code is 0 for an ok run, and
non-zero for a failed or timed-out run. `--json` prints the result
as a JSON object.

The CLI's HTTP client timeout for this call now exceeds the daemon's
own wait, so the CLI does not give up before the daemon replies.
Update the Manual run section: the result block, how to read each
field, the exit codes, and the timeout case.

Add a subsection on testing a reminder inside a session: an agent
can call set_reminder with a short one-shot schedule and delivery
kind current_session, and the reminder fires back into the same
conversation.

Bump the netclaw-operations skill version.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tui Terminal UI (Termina) issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant