Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions BACKLOG_PARKING_LOT.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ explicitly changes priority.
- Fixed-length approval button labels and richer approval UI.
- Config hot-reload beyond startup-time configuration.
- Operator diagnostics refinements beyond current CLI/doctor/status work.
- Unattended approval escalation for reminders/webhooks: pause the autonomous
run, route approval prompts to a live operator channel, and resume under the
original autonomous execution context.

## LATER Candidates

Expand Down
4 changes: 3 additions & 1 deletion docs/prd/PRD-008-scheduling-and-periodic-tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ The agent and CLI SHALL support:
- Pause/resume individual tasks
- Delete tasks
- Show execution history for a task
- Trigger an existing task to run immediately via CLI for testing and debugging

### SCHED-006 Tool Grants

Expand All @@ -156,7 +157,6 @@ Deferred to Phase 2.

- Event triggers (fire on channel message matching regex)
- Webhook triggers (fire on incoming POST)
- Manual triggers (fire via CLI only)
- One-shot scheduled tasks (fire once at a specific time)
- Per-routine persistent state (JSON blob per task)
- Heartbeat system
Expand All @@ -171,6 +171,8 @@ Deferred to Phase 2.
6. Tasks with ungrantable tools are rejected at creation.
7. Consecutive failures pause the task and notify the operator.
8. Max concurrent execution limit is enforced.
9. CLI can trigger an enabled scheduled task to run immediately without changing
its existing schedule.

## Cross-References

Expand Down
11 changes: 8 additions & 3 deletions feeds/skills/.system/files/netclaw-operations/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: netclaw-operations
description: "REQUIRED when the user asks about scheduling, reminders, cron jobs, timers, background jobs, diagnostics, troubleshooting, MCP tools, daemon health, identity updates, or Netclaw capabilities and self-maintenance."
metadata:
author: netclaw
version: "2.24.0"
version: "2.24.2"
---

# Netclaw Operations
Expand Down Expand Up @@ -47,10 +47,15 @@ allowed roots); the project's identity file (`.netclaw/AGENTS.md`, `CLAUDE.md`,

Reminders: `set_reminder` with schedule type `once` / `interval` / `cron`. Always
set `delivery_kind` explicitly (`current_session` / `channel` / `none`). A reminder
that fires unattended cannot answer approval prompts, so pre-approve any shell verbs
it needs first with `netclaw approvals trust-verb <verb>`. Background shell: set
that fires unattended cannot currently answer approval prompts, so pre-approve any
obvious shell verbs it needs first with `netclaw approvals trust-verb <verb>`.
Background shell: set
`_background: true` on `shell_execute` (max 5 concurrent; cancel servers/watchers
when done; background jobs are killed when the session passivates).
Operators can run an enabled reminder immediately with `netclaw reminder run <id>`;
this requires the daemon and does not change the reminder's schedule. After creating
a complex out-of-session reminder, offer a manual fire-now run; never offer this for
`current_session` reminders, and do not describe it as approval collection.

Full detail — delivery contract, proactive channel messaging, approval scoping,
job lifecycle — is in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,36 @@ netclaw reminder cancel <id> # disable, keep definition
netclaw reminder delete <id> # permanent delete + history
```

To run an enabled reminder immediately without changing its schedule, use:

```
netclaw reminder run <id>
```

Manual runs go through the same execution pipeline and write normal history with
`source=manual`, but they do not reschedule cron reminders, consume one-shot
occurrences, or count toward scheduled consecutive-failure auto-disable. The
daemon rejects manual runs when scheduling is disabled, the reminder is missing
or disabled, the recurring reminder has expired, the same reminder is already
executing, or the global reminder execution limit is full.

After creating a reminder, decide whether to offer an immediate fire-now run:

- **Never offer it for `delivery_kind=current_session`.** Those reminders are
conversational check-backs in the active session, not unattended execution.
- **Offer it for complex out-of-session reminders** — `delivery_kind=channel` or
`none` with shell commands, subagents, external APIs, channel delivery,
state/dedup files, or pre-approved verbs. Ask plainly: "This reminder has a few
moving parts. Want me to run it once now with `netclaw reminder run <id>` so we
can exercise the autonomous path before the scheduled fire?"
- **Do not call it a simulated dry run.** `netclaw reminder run <id>` performs a
real execution through the reminder pipeline. Warn the user if the reminder may
post to a channel, write files, call external services, or mutate state.
- **Do not promise approval collection.** Current unattended reminder executions
cannot route approval prompts back to a live operator. A manual fire-now run can
expose missing approvals by failing visibly, but it does not solve unattended
approval escalation.

Reminders that hit 5 consecutive execution failures are auto-disabled with a
`ReminderAutoDisabled` critical alert. The definition stays on disk so the
operator can diagnose and re-enable after fixing the root cause.
Expand Down
2 changes: 2 additions & 0 deletions openspec/changes/manual-reminder-execution/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-06-28
115 changes: 115 additions & 0 deletions openspec/changes/manual-reminder-execution/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
## Context

Reminder executions are currently driven by Akka.Reminders envelopes delivered to
`ReminderManagerActor`. The manager resolves the persisted `ReminderDefinition`,
applies runtime gates, starts `ReminderExecutionActor`, and uses envelope ack / redelivery
semantics for scheduled `CurrentSession` deliveries.

Issue #1511 needs a debugging path that executes the same persisted reminder now
without waiting for the next scheduled fire. The path must be fast for operators
but must not disturb the scheduler state, bypass stored trust context, or make
manual diagnostic failures count as scheduled production failures.

## Goals / Non-Goals

**Goals:**

- Add an operator CLI/API path to run an existing enabled reminder immediately.
- Reuse the existing reminder execution actor and persisted reminder definition.
- Keep durable schedules and `nextFire` unchanged.
- Preserve scheduled envelope ack/redelivery behavior for real scheduler fires.
- Distinguish manual and scheduled runs in manager accounting and history.

**Non-Goals:**

- No `--allow-disabled` behavior in this change.
- No idempotency key or retry de-duplication for repeated CLI/API calls.
- No arbitrary prompt override or one-off unsaved reminder execution.
- No dashboard, Slack slash command, or TUI surface.
- No unattended approval escalation. If a manual or scheduled autonomous run hits
an approval gate today, this change may surface that failure sooner, but it does
not route the prompt back to a live operator channel.

## Decisions

### D1. Manual runs enter through an explicit manager command

Add `RunReminderNowCommand(ReminderId, Authorization)` to the external reminder
actor protocol. The daemon endpoint sends this command to `ReminderManagerActor`;
the manager rejects missing authorization context, loads the persisted definition,
and starts execution directly.

**Rationale:** This keeps the control-plane request inside the actor that already
owns duplicate execution, concurrency, expiry, and definition lookup. It avoids
touching Akka.Reminders storage for a diagnostic action.

**Alternative rejected:** create a temporary Akka.Reminders schedule or fake
`ReminderEnvelope`. A temporary schedule can collide with the durable reminder
key or alter scheduler state; a fake envelope would make ack/redelivery behavior
lie about scheduler-owned state.

### D2. Execution origin is first-class

Add an execution source (`scheduled` or `manual`) to the execution start path,
internal completion message, and history record. Scheduled fires use
`scheduled`; CLI/API run-now uses `manual`.

**Rationale:** The manager must know whether completion should update scheduled
failure counters and one-shot lifecycle. History also needs this signal so
operators do not mistake a diagnostic run for a scheduled fire.

### D3. Manual runs fail fast rather than queueing

When the same reminder is active or the global concurrency limit is full, manual
run requests are rejected with a structured busy response. They are not added to
the scheduled deferred queue.

**Rationale:** The CLI command is a debugging action; a later queued run gives
operators less certainty and can mask the state they were trying to inspect.
Scheduled fires keep the existing deferred/duplicate semantics.

### D4. Manual completion does not change scheduled failure accounting

Manual successes and failures are written to history but do not increment, reset,
or trip the scheduled consecutive-failure counter. Manual one-shot completion does
not disable the reminder.

**Rationale:** Diagnostic attempts should not auto-disable production reminders or
erase production failure state. Operators can inspect the manual history entry to
confirm a fix without mutating scheduled accounting.

### D5. REST endpoint uses existing daemon auth plus Operator classification

`POST /api/reminders/{id}/run` is placed under the existing authorized reminder
route group and rejects callers that do not map to `PrincipalClassification.Operator`.

**Rationale:** Running a reminder can invoke tools and send messages under stored
trust context. Triggering it is therefore an operator control-plane action, not a
general authenticated-user action.

## Risks / Trade-offs

- **HTTP retry can duplicate a manual run after a fast completion** -> Accept for
MVP; add explicit run idempotency later if operators need scripted retries.
- **A scheduled fire arriving during a manual run is skipped by the existing
duplicate guard** -> Documented trade-off: non-overlap is safer than parallel
reminder execution. Status exposes skipped scheduled fires.
- **History files with old records lack source** -> Default missing source to
`scheduled` during read and add a regression test.
- **Manual run of CurrentSession has no scheduler redelivery** -> Intentional;
failures surface through the manual response/history path and can be retried by
the operator.

## Migration Plan

No config or reminder definition migration is required. Existing history JSONL
records remain readable and default to `scheduled` source when the field is
absent. Rollback leaves new records with an extra `source` JSON field; older code
that ignores unknown JSON properties can continue reading the other fields.

## Open Questions

- Should a follow-up add `--allow-disabled` for operators who intentionally want
to test a paused reminder without re-enabling it?
- Should scripted/API users get an optional idempotency key after the first CLI
workflow proves useful?
79 changes: 79 additions & 0 deletions openspec/changes/manual-reminder-execution/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
## Why

Operators debugging reminders currently have to wait for the next scheduled fire
to verify permission, delivery, and execution fixes. Issue #1511 asks for a
daemon-backed way to run an existing reminder immediately so reminder DX issues
can be diagnosed with the same speed as webhook payload replay.

## Source PRDs

- `PRD-008` (Scheduling and Periodic Tasks): scheduled task management,
execution history, and failure handling.
- `PRD-004` (CLI Onboarding and Configuration): operator-facing CLI management
surfaces that talk to the daemon.
- `PRD-002` (Gateway Security Envelope): default-deny, operator-authorized
control-plane actions, and fail-closed execution behavior.

## What Changes

- Add an operator-only immediate execution path for existing reminders:
`netclaw reminder run <id>` backed by `POST /api/reminders/{id}/run`.
- The daemon asks `ReminderManagerActor` to run the persisted reminder definition
through the normal `ReminderExecutionActor` pipeline without creating a durable
Akka.Reminders schedule and without synthesizing a fake scheduler envelope.
- Manual execution leaves the reminder's existing schedule untouched: no
`nextFire` mutation, no cron reschedule, and no one-shot consumption.
- Manual runs respect runtime safety gates: scheduling disabled, missing
reminder, disabled reminder, expired recurring reminder, duplicate in-flight
execution, and global concurrency exhaustion all fail loudly.
- Execution records carry whether the run was scheduled or manual so operators
can interpret history and status output correctly.

In scope for MVP:

- CLI, REST API, actor protocol, execution origin tracking, history output, and
tests for success/failure gates.
- Manual runs of enabled reminders only.

Out of scope for MVP:

- Running disabled reminders via `--allow-disabled`.
- Idempotency keys for retried HTTP/manual requests.
- Replaying arbitrary historical reminder payloads or overriding the persisted
reminder definition at run time.
- A dashboard or Slack slash command for manual execution.

## Capabilities

### New Capabilities

None.

### Modified Capabilities

- `netclaw-scheduling`: immediate manual execution semantics, safety gates, and
scheduled-vs-manual execution accounting.
- `netclaw-cli`: add `netclaw reminder run <id>` as an operator command that
requires a running daemon.
- `reminder-execution-history`: execution records include the run source so
manual diagnostic runs are distinguishable from scheduler fires.

## Impact

- **Actor protocol:** add an external `RunReminderNowCommand` with authorization
context and response; add execution origin to the internal completion message.
- **Runtime:** `ReminderManagerActor` starts manual executions directly through
`ReminderExecutionActor` with no Akka.Reminders envelope. Scheduled execution
paths continue to use existing envelope ack/redelivery behavior.
- **REST API:** add `POST /api/reminders/{id}/run`, protected by the existing
daemon authorization policy and restricted to Operator authority.
- **CLI:** add `netclaw reminder run <id>` and help text; daemon offline and
non-success responses produce clear errors.
- **Persistence/history:** append `source` to new history records while retaining
compatibility with existing JSONL records that lack the field.
- **Security:** no new tool grants or bypasses. Manual execution uses the stored
reminder audience/boundary and the same delivery/tool policy as scheduled
execution; the control-plane trigger is operator-only.
- **Operations:** operators can test permission and delivery changes immediately;
manual failures are visible in history but do not count toward scheduled
auto-disable thresholds.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
## ADDED Requirements

### Requirement: CLI command for immediate reminder execution

The CLI SHALL provide a `netclaw reminder run <id>` subcommand that asks the
running daemon to trigger immediate execution of an existing reminder. The
subcommand SHALL require a daemon connection, SHALL call the daemon reminder run
endpoint, and SHALL print a clear success or failure message.

The command SHALL NOT edit reminder files directly and SHALL NOT attempt to run a
reminder when the daemon is offline.

#### Scenario: Operator runs reminder immediately

- **GIVEN** the daemon is running
- **WHEN** the operator runs `netclaw reminder run daily-summary`
- **THEN** the CLI calls `POST /api/reminders/daily-summary/run`
- **AND** exits with code `0` when the daemon accepts the run
- **AND** prints a message naming the reminder and indicating the run started

#### Scenario: Daemon-required failure is clear

- **GIVEN** no daemon API is available to the CLI
- **WHEN** the operator runs `netclaw reminder run daily-summary`
- **THEN** the CLI exits non-zero
- **AND** prints that `reminder run` requires a running daemon

#### Scenario: Daemon rejection is surfaced

- **GIVEN** the daemon rejects immediate execution because the reminder is disabled, busy, missing, or scheduling is disabled
- **WHEN** the operator runs `netclaw reminder run daily-summary`
- **THEN** the CLI exits non-zero
- **AND** prints the daemon-provided reason without claiming the run started
Loading
Loading