Create and migrate repo-defined Tessl schedules in a project's tessl.json. Turns a plain-language task and cadence into a validated schedules entry, optionally authors its repo-local skill, and moves an existing UI/CLI schedule into version control.
89
96%
Does it follow best practices?
Impact
90%
1.01x2 of 3 eval scenarios. Add 1 more for a full score.
Passed
No findings from the security scan
schedules block in tessl.jsonThe schedules block is a top-level object in a project's tessl.json. It maps
a schedule key to a schedule entry. Each entry declares one recurring
launch run: action says what runs, and the fields beside it say when.
{
"schedules": {
"<schedule-key>": {
"action": {
"skill": "...",
"environment": "..."
},
"schedule": "weekdays at 09:00"
}
}
}a-z, 0-9, single dashes, 1 to 64 chars
(pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$).The entry is validated strictly: a field name that is not in these lists is
a hard error (this catches typos the author would otherwise only notice when the
schedule misbehaves). An action field written beside action instead of inside
it is refused too.
Beside action, an entry carries only its own timing and label:
| Field | Type | Notes |
|---|---|---|
action | object or string | Required. What the schedule runs, in one of the forms under The action field. |
schedule | string or object | Preferred authoring key. Accepts a supported shorthand string, a raw cron string, or a structured schedule object. The value is interpreted in timezone. |
cron | string or object | Alias for schedule. Use it for a raw cron fallback when the shorthand and structured forms cannot express the cadence. |
timezone | string | Optional, default UTC. IANA timezone the schedule is interpreted in (e.g. America/New_York). |
description | string | Optional. Human-readable description of the schedule (max 4096 chars). |
Use exactly one of schedule or cron, never both. Both forms are compiled to
canonical cron before timezone and minimum-interval validation. A shorthand or
structured value is easier for humans to review, so prefer schedule for new
entries.
The supported shorthand strings are:
| Form | Example |
|---|---|
hourly | hourly |
daily at HH:MM | daily at 02:00 |
weekdays at HH:MM | weekdays at 09:00 |
weekly on DAY at HH:MM | weekly on monday at 13:00 |
monthly on Nth at HH:MM | monthly on 1st at 00:00 |
every N minutes | every 15 minutes |
every N hours | every 6 hours |
every N minutes between HH:MM-HH:MM | every 15 minutes between 09:00-17:00 |
every N minutes on weekdays | every 15 minutes on weekdays |
For a cadence that does not fit a shorthand, use a structured object. Each field is optional and omitted fields match any value:
{
"schedule": {
"minute": [0, 30],
"hour": 9,
"day": {"between": [1, 7]},
"month": ["january", "jun"],
"weekday": ["mon", "wed", "fri"]
}
}Numeric fields (minute, hour, and day) accept a number, a list of
numbers, { "every": number }, or { "between": [start, end] }. month and
weekday accept numbers, lists, or supported names such as january and
monday. Use a raw five-field cron under cron when the structured object
cannot express the requested pattern.
action fieldaction takes one of three forms:
"action": "nightly-report",
naming an action in the manifest's top-level actions block.{ "use": "nightly-report", ... }. The
given fields merge over the declaration key by key, null removes a field
other than skill,
and inputs merges per input name.The action fields, written inside action:
| Field | Type | Default | Notes |
|---|---|---|---|
skill | string | none | Required in an inline action. The skill to run: a registry ref workspace/plugin[@version][#skill], or a repo-local file:path. Validated server-side at apply time. |
environment | string | none | The workspace environment name (not its id). The id is workspace-scoped, so a committed file names the environment and the apply step resolves it to an id in the target workspace. |
baseBranch | string | Loop default branch | Branch each fired run clones and targets its PR against. |
workdir | string | repo root | Subdirectory of the repo the skill runs in. |
agent | string | workspace default | Agent to run the skill with. Plain string, validated server-side. |
model | string | agent default | Model to run the skill with. Plain string, validated server-side. |
effort | string | agent default | Reasoning effort: minimal, low, medium, high, xhigh, or max. Which levels an agent offers is checked at apply time. |
inputs | object | none | Skill input values, keyed by the skill's own placeholder names. String values only; keys must be non-empty. |
instructions | string | none | Free-text instructions appended to the skill prompt. |
Two schedules that run the same skill can share one declaration:
{
"actions": {
"pr-report": { "skill": "file:tiles/pr-report", "environment": "reports" }
},
"schedules": {
"pr-report-nightly": { "action": "pr-report", "cron": "0 2 * * *" },
"pr-report-weekly": {
"action": { "use": "pr-report", "instructions": "Cover the last 7 days." },
"cron": "0 4 * * 1"
}
}
}An older entry that writes the action's fields beside cron, with no action,
still runs and is read as the inline action it describes.
There is no file field that pins a commit or tag. Each fire clones the current
head of baseBranch. When baseBranch is omitted, the run uses the linked
project's Loop default branch, or the repository default when no project is
linked. The project setting reads the repository default at fire time unless
it names a branch.
tessl schedule validate does and does not checkvalidate reads the file and checks the block's shape, compiles each
schedule or cron value, validates the resulting expression against its
timezone, and checks the minimum 5-minute interval. It also checks that
every action a schedule names, directly or with use, is declared in the
actions block, and validates the whole actions block itself. A problem
in one declaration is reported against that action (actions.<name>); a
problem with the block as a whole, such as an actions value that is not an
object, is reported as actions block. It exits non-zero when either block is
invalid, so it works as a CI check. A manifest with neither block is valid.
It does not check (these are decided at apply time, in the workspace):
skill ref resolves to a plugin the workspace can use;environment exists in the workspace or carries a GitHub
token;agent, model, and effort are in the organization's catalogue;So a file that passes validate can still be rejected when applied. A clean
validate means the file is well-formed, not that the schedule will fire.
{
"schedules": {
"weekly-dependency-digest": {
"action": {
"skill": "tessleng/dependency-digest",
"environment": "dependency-digest",
"agent": "claude",
"model": "claude-sonnet-4-6"
},
"schedule": "weekly on monday at 13:00",
"timezone": "UTC",
"description": "Weekly dependency digest, Mondays 13:00 UTC"
}
}
}