|
| 1 | +# Trigger sources |
| 2 | + |
| 3 | +A scheduled job runs when its **trigger** fires. Until 0.4 the only trigger was a |
| 4 | +clock, so "every weekday at 09:00" was expressible and "when the release meeting |
| 5 | +starts" or "when the schema changes" were not — both of which are scheduling, and |
| 6 | +neither of which is a time. |
| 7 | + |
| 8 | +```jsonc |
| 9 | +// ~/.deepcode/cron.json |
| 10 | +{ |
| 11 | + "jobs": [ |
| 12 | + { "id": "nightly", "schedule": "0 3 * * *", "prompt": "…", "cwd": "/repo" }, |
| 13 | + |
| 14 | + { |
| 15 | + "id": "release-prep", |
| 16 | + "trigger": { "kind": "ics", "path": "team.ics", "match": "release" }, |
| 17 | + "prompt": "Run the release checklist", |
| 18 | + "cwd": "/repo", |
| 19 | + }, |
| 20 | + |
| 21 | + { |
| 22 | + "id": "regen", |
| 23 | + "trigger": { "kind": "file", "paths": ["schema.json"] }, |
| 24 | + "prompt": "Regenerate the client from schema.json", |
| 25 | + "cwd": "/repo", |
| 26 | + }, |
| 27 | + ], |
| 28 | +} |
| 29 | +``` |
| 30 | + |
| 31 | +`schedule` still works and still means cron. A job written before triggers |
| 32 | +existed needs no migration — a store nobody has to rewrite cannot be rewritten |
| 33 | +wrongly. |
| 34 | + |
| 35 | +## Everything is polled |
| 36 | + |
| 37 | +`deepcode scheduler run` already wakes on a timer and asks what is due. Every |
| 38 | +trigger answers that same question, so there is no daemon, no watcher process, |
| 39 | +and no way for a trigger to fire while nothing is listening. |
| 40 | + |
| 41 | +The cost is **minute granularity** for all of them, which is the granularity you |
| 42 | +can observe anyway: a trigger resolvable to the second would fire or not |
| 43 | +depending on how promptly launchd got around to it. |
| 44 | + |
| 45 | +## `cron` |
| 46 | + |
| 47 | +```jsonc |
| 48 | +{ "kind": "cron", "schedule": "0 9 * * 1-5" } |
| 49 | +``` |
| 50 | + |
| 51 | +Five fields: minute, hour, day-of-month, month, day-of-week. |
| 52 | + |
| 53 | +## `ics` — a calendar file |
| 54 | + |
| 55 | +```jsonc |
| 56 | +{ "kind": "ics", "path": "team.ics", "match": "release" } |
| 57 | +``` |
| 58 | + |
| 59 | +Fires in the minute a matching event **starts**. `match` is a case-insensitive |
| 60 | +substring of the event summary; without it, every event in the file fires the |
| 61 | +job. |
| 62 | + |
| 63 | +**Standard iCalendar text is the only calendar input.** No vendor SDK, no OAuth |
| 64 | +to anybody's calendar service, no remote account polling. Every calendar worth |
| 65 | +integrating with exports `.ics`, and a file on disk is a boundary you can |
| 66 | +inspect — which a vendor client library is not. Point the path at an export, a |
| 67 | +synced file, or something your own tooling writes. |
| 68 | + |
| 69 | +### What the reader supports |
| 70 | + |
| 71 | +| Construct | Behaviour | |
| 72 | +| ------------------------------ | ---------------------------------------------------- | |
| 73 | +| `DTSTART` UTC (`…Z`) | Used as written | |
| 74 | +| `DTSTART` floating (no zone) | Read as UTC | |
| 75 | +| `DTSTART;VALUE=DATE` (all-day) | **Never fires** — see below | |
| 76 | +| `SUMMARY`, incl. folded lines | Used for `match` | |
| 77 | +| `RRULE FREQ=DAILY` / `=WEEKLY` | Expanded, with `INTERVAL`, `BYDAY`, `UNTIL`, `COUNT` | |
| 78 | +| Anything else | **Reported**, never silently dropped | |
| 79 | + |
| 80 | +Unsupported constructs are logged next to the job that hit them. A silently |
| 81 | +ignored `RRULE` is a job that never fires, and that failure looks exactly like |
| 82 | +"nothing was scheduled" — which is the one thing it must not be mistaken for. |
| 83 | + |
| 84 | +`TZID` is reported rather than honoured. DeepCode carries no timezone database, |
| 85 | +and quietly applying the host's zone would make the same file fire at different |
| 86 | +moments on different machines. |
| 87 | + |
| 88 | +**All-day entries never fire.** They name a day, not a moment; choosing one |
| 89 | +(midnight? 09:00?) would be DeepCode inventing a schedule you did not write. Use |
| 90 | +a `cron` trigger if you want a time. |
| 91 | + |
| 92 | +## `file` — something changed |
| 93 | + |
| 94 | +```jsonc |
| 95 | +{ "kind": "file", "paths": ["schema.json", "proto/"] } |
| 96 | +``` |
| 97 | + |
| 98 | +Fires when any listed path's modification time is newer than the job's last run. |
| 99 | +Relative paths resolve against the job's `cwd`. |
| 100 | + |
| 101 | +Two behaviours worth knowing: |
| 102 | + |
| 103 | +- **The first evaluation never fires.** It records a baseline instead. Otherwise |
| 104 | + every file trigger would fire the moment it was created, on files nobody had |
| 105 | + touched since anyone cared. |
| 106 | +- **A missing path is not a change.** A watched file may simply not have been |
| 107 | + generated yet, and reporting that every minute would bury the messages that |
| 108 | + matter. |
| 109 | + |
| 110 | +## Permissions are unchanged |
| 111 | + |
| 112 | +A trigger decides _when_, never _what may happen_. Every scheduled run still goes |
| 113 | +through the [trigger profile](FLOATBOAT_ADOPTION_PLAN.md) clamp: a permissive |
| 114 | +`permissions.defaultMode` inherited from interactive settings is reduced to |
| 115 | +`default` unless the job sets `profile.mode` explicitly, and a call needing |
| 116 | +approval is refused because nobody is present to give it. |
| 117 | + |
| 118 | +A calendar you do not control deciding _when_ DeepCode runs is already worth |
| 119 | +thinking about. It must never also decide what it may do. |
0 commit comments