Files
PaperClipAI/skills/paperclip/references/routines.md
DottaandPaperclip 30ff3d7c58 feat(routines): expose activity gate API (#9438)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Scheduled routines provide recurring control-plane work without
manual intervention
> - The new activity gate can suppress scheduled runs when no external
work occurred
> - The core scheduler and database support landed without a public
create/update contract
> - Agents, operators, and managed plugins need validated fields plus
discoverable semantics to opt in safely
> - This pull request exposes the activity gate through routine APIs,
revisions, plugin contracts, tests, and skill documentation
> - The benefit is backward-compatible control over idle scheduled work
without losing activity-triggered follow-up

## Linked Issues or Issue Description

- Refs #8534

## What Changed

- Added shared activity-gate policy and scope enums with create/PATCH
validation.
- Persisted activity-gate fields through routine creation, updates,
revision snapshots, pipeline snapshots, and revision restores.
- Defaulted legacy revision snapshots during restore and added
regression coverage for pre-field snapshots.
- Extended managed-plugin routine declarations, production
reconciliation, and the SDK test harness to preserve non-default gate
settings.
- Added end-to-end API coverage for create/PATCH/list/detail
round-trips, defaults, and invalid enum rejection.
- Documented schedule-only semantics, activity windows,
own-run/read-action exclusions, scopes, and an hourly quiet-night
watcher example.

## Verification

- `pnpm exec vitest run packages/shared/src/validators/routine.test.ts
server/src/__tests__/routines-service.test.ts
server/src/__tests__/routines-e2e.test.ts`
- `pnpm exec vitest run packages/shared/src/validators/plugin.test.ts
packages/plugins/sdk/tests/testing-actions.test.ts
server/src/__tests__/plugin-managed-routines.test.ts
server/src/__tests__/routines-service.test.ts -t 'activity
gate|preserves declared activity gate settings|resolves routine agent
and project refs'`
- `pnpm exec vitest run ui/src/lib/workspace-routines.test.ts
ui/src/pages/Routines.test.tsx`
- `pnpm --filter @paperclipai/shared typecheck`
- `pnpm --filter @paperclipai/plugin-sdk typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm check:token-gates`
- GitHub CI: all final-head checks green; Storybook visual regression
skipped by path rules.
- Greptile: 5/5 with no unresolved review threads.

## Risks

- Low risk: defaults remain `always` and `company`, preserving existing
routine behavior and old revision snapshots.
- Managed plugin manifests can now declare the same validated gate
settings as the public routine API; omitted values retain core defaults.
- Revision snapshots now include the new fields so policy changes are
not lost or treated as no-ops during restore.

> For core feature work, checked `ROADMAP.md`: this extends the existing
Scheduled Routines roadmap item and does not duplicate a separate
planned capability.

## Model Used

- OpenAI GPT-5.5 via Codex CLI, with repository tool use and code
execution; context-window size was not exposed by the runtime.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-24 16:47:24 -05:00

7.2 KiB

Paperclip Routines

Routines are recurring tasks. Each time a routine fires it creates an execution issue assigned to the routine's agent — the agent picks it up in the normal heartbeat flow.

A routine has:

  • One assigned agent and one project
  • One or more triggers (schedule, webhook, or api)
  • A concurrency policy (what to do when a previous run is still active)
  • A catch-up policy (what to do with missed scheduled runs)
  • An activity gate policy (whether quiet scheduled ticks should be skipped)

Authorization: Agents can read all routines in their company but can only create or manage routines assigned to themselves. Board operators have full access, including reassignment.


Lifecycle

active <-> paused
active  -> archived  (terminal — cannot be reactivated)

Paused routines do not fire. Archived routines do not fire and cannot be unarchived.


Creating a Routine

POST /api/companies/{companyId}/routines
{
  "title": "Weekly CEO briefing",
  "description": "Compile status report and post to Slack",
  "assigneeAgentId": "{agentId}",
  "projectId": "{projectId}",
  "goalId": "{goalId}",           // optional
  "parentIssueId": "{issueId}",   // optional — parent for run issues
  "priority": "medium",
  "status": "active",
  "concurrencyPolicy": "coalesce_if_active",
  "catchUpPolicy": "skip_missed",
  "activityGatePolicy": "always",
  "activityGateScope": "company"
}
Field Required Notes
title yes Max 200 chars
description no Human-readable description of the routine
assigneeAgentId yes Agents: must be themselves
projectId yes
goalId no Inherited by run issues
parentIssueId no Run issues become children of this issue
priority no critical high medium (default) low
status no active (default) paused archived
concurrencyPolicy no See below
catchUpPolicy no See below
activityGatePolicy no always (default) or require_external_activity; see below
activityGateScope no company (default) or project; see below

Concurrency Policies

Controls what happens when a trigger fires while the previous run issue is still open or active.

Policy Behaviour
coalesce_if_active (default) New run is marked coalesced and linked to the existing active run — no new issue created
skip_if_active New run is marked skipped and linked to the existing active run — no new issue created
always_enqueue Always create a new issue regardless of active runs

Catch-Up Policies

Controls what happens with scheduled runs that were missed, for example during server downtime.

Policy Behaviour
skip_missed (default) Missed runs are dropped
enqueue_missed_with_cap Missed runs are enqueued, capped at 25

Activity-Gated Scheduled Runs

activityGatePolicy controls whether a schedule trigger runs when the system has been quiet. It does not gate manual, API, or webhook runs.

Policy Behaviour
always (default) Run on every scheduled tick
require_external_activity Run only when qualifying activity occurred after this routine's last dispatched, non-skipped run

activityGateScope selects where qualifying activity is checked:

Scope Behaviour
company (default) Activity anywhere in the routine's company can wake it
project Only activity attributed to the routine's project can wake it

The activity window starts at the triggeredAt time of the last dispatched run. A routine that has never dispatched always runs once. Runs skipped for quiet activity do not advance the window, so one later qualifying event still wakes the next scheduled tick.

The gate excludes activity generated by the routine's own dispatched run issues, scheduler bookkeeping for that routine, and pure-read actions such as issue read/unread changes and inbox archive/unarchive actions. Work performed by other agents on tasks the routine delegated is external activity and wakes the routine on its next tick.

Example: skip quiet nights

This hourly watcher runs after company activity, follows up while delegated work continues, and stops consuming runs once the company settles overnight:

{
  "title": "Hourly work watcher",
  "description": "Review recent work and follow up on delegated tasks",
  "assigneeAgentId": "{agentId}",
  "projectId": "{projectId}",
  "activityGatePolicy": "require_external_activity",
  "activityGateScope": "company"
}

Add a schedule trigger with cronExpression: "0 * * * *". The first tick runs. Later ticks run only after qualifying company activity since the last dispatched run; quiet skipped ticks keep the original activity window open.


Adding Triggers

A routine can have multiple triggers of different kinds.

All trigger kinds accept an optional label field (max 120 chars), which is useful for distinguishing multiple triggers of the same kind on one routine.

POST /api/routines/{routineId}/triggers

Schedule (cron)

{
  "kind": "schedule",
  "cronExpression": "0 9 * * 1",
  "timezone": "Europe/Amsterdam"
}
  • cronExpression: standard 5-field cron syntax
  • timezone: IANA timezone string (for example UTC or America/New_York)
  • The server computes nextRunAt automatically

Webhook

{
  "kind": "webhook",
  "signingMode": "hmac_sha256",
  "replayWindowSec": 300
}
  • signingMode: bearer (default) or hmac_sha256
  • replayWindowSec: 30-86400 (default 300)
  • Response includes the webhook URL (publicId-based) and the signing secret
  • Fire externally: POST /api/routine-triggers/public/{publicId}/fire
    • Bearer: Authorization: Bearer <secret>
    • HMAC: X-Paperclip-Signature + X-Paperclip-Timestamp headers

API (manual only)

{
  "kind": "api"
}

No configuration. Fire via the manual run endpoint.


Updating and Deleting Triggers

PATCH /api/routine-triggers/{triggerId}
{ "enabled": false, "cronExpression": "0 10 * * 1" }

DELETE /api/routine-triggers/{triggerId}

To rotate a webhook secret (the old secret is immediately invalidated):

POST /api/routine-triggers/{triggerId}/rotate-secret

Manual Run

Fires a run immediately, bypassing the schedule. Concurrency policy still applies.

POST /api/routines/{routineId}/run
{
  "source": "manual",
  "triggerId": "{triggerId}",       // optional — attributes run to a specific trigger
  "payload": { "context": "..." }, // optional — passed to the run issue
  "idempotencyKey": "unique-key"   // optional — prevents duplicate runs
}

Updating a Routine

All create fields are updatable. Agents cannot reassign a routine to another agent.

PATCH /api/routines/{routineId}
{ "status": "paused", "title": "New title" }

Reading Routines and Runs

GET /api/companies/{companyId}/routines
GET /api/routines/{routineId}
GET /api/routines/{routineId}/runs?limit=50

Use the generic API endpoint tables in skills/paperclip/references/api-reference.md when you need a full cross-domain reference. Use this file when you need routine-specific behaviour, payload shape, or policy details.