CtrlK
BlogDocsLog inGet started
Tessl Logo

tessleng/schedule-management

Create and migrate repo-defined Tessl schedules in a project's tessl.json. Turns a plain-language cadence into a validated schedules entry, and moves an existing UI/CLI schedule into version control.

90

0.94x
Quality

97%

Does it follow best practices?

Impact

94%

0.94x

Average score across 2 eval scenarios

SecuritybySnyk

Low

Low-risk findings worth noting

Overview
Quality
Evals
Security
Files

SKILL.mdskills/schedule-migrate/

name:
schedule-migrate
description:
Migrate an existing UI- or CLI-created Tessl schedule into a repo-defined entry in tessl.json. Use when the user wants to move a schedule into version control ("put my schedule in the repo", "migrate this schedule to tessl.json", "make my scheduled run repo-defined"), or convert an app-defined schedule to a repo-defined one. The skill lists live schedules, reads the chosen one's config, writes the equivalent tessl.json entry, validates it, and optionally deletes the old schedule once the new one is confirmed. Not for creating a schedule from scratch (use schedule-setup).

Migrate a schedule into tessl.json

Move an existing schedule (one created in the UI or with tessl schedule create) into the project's tessl.json, so it is reviewed and version-controlled next to the code. Then, once the repo-defined schedule is confirmed working, delete the old one.

Read the field mapping reference before writing the entry. It maps live-schedule config fields to file fields and calls out the fields the read API does not expose.

Communication rules

  • Ask about one decision at a time.
  • Explain each concept in one or two plain sentences.
  • Never delete the old schedule until the user has confirmed the repo-defined one is applied and working. Deletion is the last, explicit step.

1. Preflight

  • Work from the repository root, where tessl.json lives.
  • Confirm tessl.json exists. If not, tell the user to run tessl init first and stop.
  • Read the current tessl.json. Note any existing schedules block and its keys, so the migrated entry does not collide.
  • Check the current CLI surface with tessl schedule --help.

2. List existing schedules

tessl schedule list --json --limit 100

Show the user a concise list. The key details are the schedule id, its cron, the skill it runs, and its status. If the user works across workspaces, pass --workspace <name> to scope the list.

Note each schedule's definition source if the field is present: a schedule already marked repo is defined by a tessl.json and does not need migrating. Only app-defined (UI/CLI-created) schedules are candidates.

If the list contains 100 items, there may be more. Tell the user the listing may be partial and let them name a schedule by id if it did not appear.

3. Pick one to migrate

Ask which schedule to migrate. Migrate one at a time. It keeps the mapping and the confirmation step clear.

4. Read its configuration

tessl schedule view <id> --json

Extract the fields you need for the file entry. See the field mapping reference for the exact source-to-file mapping. In short:

  • cron maps to cron
  • timezone maps to timezone (omit if UTC)
  • config.skillRef maps to skill
  • environment.name maps to environment (the file names the environment; the id in the live config is workspace-scoped and not portable)
  • config.baseBranch maps to baseBranch (omit if main)
  • config.snapshot maps to snapshot
  • config.workdir maps to workdir
  • description maps to description

agent and model require an explicit decision. schedule view returns the resolved values but does not indicate whether they were explicitly set or inherited from a workspace default. Ask the user: "The live schedule shows agent <value> and model <value>. Were these set explicitly, or inherited from workspace defaults?" Set the fields only if the user confirms they were set explicitly or wants to preserve them. Omitting them lets the repo-defined schedule pick up current workspace defaults.

Fields the read API does not return. A schedule's inputs and instructions are hidden on the read paths because they can contain sensitive values such as credentials, tokens, or personal data. view cannot recover them.

Before asking the user for these values, warn them: inputs and instructions will be stored in tessl.json, which is committed to the repository and visible to everyone with repo access, including history. Ask whether the values are safe to commit. If they contain credentials or personal data, the user should instead store them in the workspace environment and reference them via the environment's variables, rather than embedding them directly in the file. Only add them to the entry if the user confirms they are safe to commit, or confirms they are not needed.

Fields with no file equivalent. timeoutMs, label, sandboxSnapshot, and stored metadata are not part of the schedules file schema and are dropped in a repo-defined schedule. Tell the user if the source schedule used a non-default timeoutMs or a label, so the drop is a decision, not a surprise.

Paused source schedule. Check the source schedule's status field. If it is paused, flag this explicitly: repo-defined schedules cannot be created in a paused state: a new repo entry will be active immediately on apply. Ask the user how to proceed. Options: migrate it as active (the schedule will start firing), skip the migration, or proceed with migration and immediately pause the old schedule after.

5. Scope of reconciliation

Before writing the entry, tell the user: Tessl's GitHub App reconciles tessl.json against every workspace reachable by its installation. If the app is installed across multiple workspaces, pushing the entry will attempt to create the schedule in each workspace where its named environment exists. A workspace where that environment is absent will skip it without error. The user should confirm their GitHub App installation scope covers only the intended workspaces, or name the environment such that it only exists in the right one.

6. Choose a schedule key

The file entry needs a lowercase kebab-case key (a-z, 0-9, single dashes) as its stable identity. Derive it from the schedule's job or label (e.g. nightly-sales-report). It must not collide with an existing key in the block.

7. Write the entry

Add the entry under the top-level schedules object in tessl.json (create the object if absent). Preserve the rest of the file. skill, cron, and environment are required; the block is validated strictly, so only use the fields in the mapping reference. Example:

{
  "schedules": {
    "nightly-sales-report": {
      "skill": "file:tiles/dark-factory-report",
      "cron": "0 2 * * *",
      "environment": "dark-factory-report",
      "agent": "claude",
      "model": "claude-sonnet-4-6",
      "description": "Nightly sales report"
    }
  }
}

8. Validate

tessl schedule validate

Fix anything it reports and rerun until it passes. validate checks the file's shape, cron, and 5-minute-minimum interval only, not whether the environment exists, the skill installs, or the agent/model are in the catalogue. Those are checked when the file is applied.

9. Apply, confirm, then delete the old schedule

This order matters: verify the repo-defined schedule works before removing the old one, so a mistake never leaves the job unscheduled. However, the two schedules must not run concurrently once the repo one is active, because duplicate execution can duplicate side effects.

  1. Have the user commit the tessl.json change and push it to the repo's default branch. On that push, Tessl reconciles the file and creates the repo-defined schedule.

  2. Immediately after the push, before the next cron window fires, pause or delete the old app-defined schedule:

    tessl schedule delete <old-id>

    Use the old app-defined schedule's id from the listing in step 2, not the new repo-defined one. Deleting is irreversible, so confirm the id with the user before running it. If the user wants a safety net, they can pause instead (if the CLI supports it), but pausing still risks firing if the next cron fires before the pause takes effect. Deletion is the safer choice.

  3. Confirm the repo-defined schedule appeared:

    tessl schedule list --json

    The migrated schedule should now show with definition source repo.

If the repo-defined schedule never appears after the push, restore the old schedule if you deleted it (tessl schedule create with the original parameters), or unpause it. The usual causes of a missing repo schedule are a push to a non-default branch, a missing environment in the workspace, or the feature not being enabled.

skills

schedule-migrate

README.md

tile.json