# Schedule

> Canonical path: `specs/schedule/schedule.md`
> Status: living-spec
> Scope: time-based AgentTeam schedule definitions, occurrences, AgentTeam run ledger,
> message effects, task-discussion effects, and generic external-trigger outbox
> infrastructure.
>
> Related specs:
> - [`specs/architecture/service-actor-event-automation.md`](../architecture/service-actor-event-automation.md)
> - [`specs/automation/run-effect-contract.md`](../automation/run-effect-contract.md)
> - [`specs/collaboration/source-projection-delivery.md`](../collaboration/source-projection-delivery.md)
>
## 1. Purpose

Schedule is a AgentTeam-owned timing and delivery product. It expands time sources,
reserves one durable AgentTeam run per occurrence, writes AgentTeam-owned effects such as
messages or task discussions, and records idempotency, retry, and failure state.

MeshX Automation is a separate MeshX-owned domain. AgentTeam Server must not parse
Automation definitions, validate Automation DSL, inspect Automation secrets,
construct MeshX Automation business payloads, or own a MeshX client. Browser and
API access to MeshX Automation goes through the `internal/forwarder` pass-through
boundary. If AgentTeam needs to trigger an external interface from a scheduled
occurrence, it may use generic outbox infrastructure with opaque destination and
payload data, but the outbox contract must not encode MeshX Automation source,
permission, preflight, or runtime semantics.

## 2. Definition

A schedule definition records:

```text
workspace
owner / principal
name
status
source_kind = time
source_type = one_shot_absolute | one_shot_relative | interval | cron
scheduled_at / relative_seconds / interval_seconds / cron_expression / timezone / anchor_at
message_template
target kind and target ref
target snapshot
message delivery options such as also_start_thread for channel targets
created_by
metadata
```

Schedules are not service actors and are not message authors. Current schedule
API targets are AgentTeam routable surfaces such as channels, reply threads, direct
messages, and task scopes. The public schedule/trigger API does not accept a
MeshX Automation target or Automation definition fields.

## 3. Execution

A due occurrence reserves a AgentTeam run:

```text
schedule_definition
  -> automation_run(automation_kind=schedule, source_kind=time)
  -> AgentTeam-owned effect (message, task discussion, or none for external outbox handoff)
```

The delivered message source is:

```text
kind = schedule_run
refType = automation_run
refId = automation_run.id
```

For transcript message effects, the automation run's message link must commit in
the same database transaction that makes the message visible. Terminal run
completion may follow immediately afterward, but message reads must never
temporarily fall back to the schedule owner's human-compose source.

For ordinary AgentTeam targets, the scheduler writes the visible effect and completes
the run in the same effect contract. When a channel target has
`also_start_thread` enabled, the scheduled message uses the same ordinary channel
send path that can open a reply thread from the root message; public-agent
mentions remain channel-delivered, and private-agent mentions keep the normal
thread retargeting behavior.

```text
status = succeeded | failed | retry_waiting
effect_kind = message | task_discussion | none
effect_ref = message id or task-discussion entry id when an effect is written
```

If a future schedule target triggers an external interface, the reservation may
create a generic outbox row in the same database transaction as the AgentTeam run.
The outbox row is responsible only for delivery state, idempotency, leasing,
retry, and final completion. Domain-specific payload construction and validation
must live at the external domain boundary, not inside AgentTeam schedule management.

## 4. Idempotency

Every reserved run has a deterministic idempotency key:

```text
schedule:{schedule_definition_id}:{occurrence_rfc3339nano}
```

The run ledger is the idempotency anchor for both direct AgentTeam effects and any
generic outbox handoff. Retries must reuse the same run id and idempotency key;
they must not create duplicate transcript messages or duplicate external
triggers for the same logical occurrence.

## 5. Ownership and authorization

AgentTeam owns:

- time expansion and next-occurrence calculation;
- schedule/trigger CRUD and validation for AgentTeam routable targets;
- fire-time permission checks for AgentTeam message/task-discussion effects;
- run status, retry, stale-run recovery, and compatibility views;
- generic outbox delivery state when a AgentTeam schedule triggers an external
  interface.

MeshX owns Automation-specific behavior, including definition source paths such
as `.agentteam/**/*.yml|*.yaml`, `manual` triggers, DSL validation, secrets,
Automation permissions, runner assignment, job ledgers, waits, logs, artifacts,
and Automation run status. AgentTeam Server reaches MeshX Automation only through the
forwarder pass-through API surface.

## 6. Failure handling

AgentTeam run failures use stable error codes suitable for schedule users and
operators:

- `target_not_found`
- `forbidden_current_permissions`
- `content_invalid`
- `transient_write_error`
- `source_kind_not_enabled`
- `trigger_not_active`
- `trigger_completed`
- `invalid_trigger_definition`

External outbox delivery failures should map transport and retry exhaustion to
AgentTeam run errors without exposing external-domain internal details as AgentTeam Server
APIs.

## 7. Compatibility

Legacy `/triggers` tools and APIs remain compatibility aliases over the canonical
schedule model. They must return the same AgentTeam target, timing, status, run, and
validation data as the schedule APIs. MeshX Automation authoring and run APIs are
not modeled as trigger targets; clients should use the MeshX Automation
forwarder surface directly.

## 8. User experience

Users configure schedules by choosing a time source, a AgentTeam target, and message
content. Validation resolves the AgentTeam target, checks that the schedule can run in
the future, and verifies the message content against current permissions.

Target-scoped management surfaces must request target filtering from the server.
A reply-thread request returns only that reply thread's schedules. A channel or
DM request may explicitly include schedules for its active child reply threads;
the client must not fetch the workspace list and reconstruct this scope locally.

Run history should show each scheduled occurrence, status, retry/failure detail,
and the resulting AgentTeam effect. External-domain runtime detail belongs in that
domain's UI or forwarder-backed views. Agent Runtime run-history reads are
bounded keyset pages ordered by the effective
`COALESCE(attemptedAt, createdAt) DESC, id DESC` key; unattempted rows expose
`createdAt` through the stable `attemptedAt` field for cursor continuity. They
default to 20 rows, cap a page at 100 rows, and return `{runs, hasMore,
nextCursor}`. Empty and final pages use an empty `runs` array with
`hasMore: false`, while malformed
or non-canonical cursors return the normal validation error envelope.
