-
Notifications
You must be signed in to change notification settings - Fork 3
plat 144
PLAT-144 — schedules that logically depend on another schedule can only use wall-clock offsets, so normal runtime variance makes them collide
| Coordination | Value |
|---|---|
| Assigned agent | Codex |
| Ticket state |
implemented and regression-tested — durable occurrence-linked fan-in, terminal policy, delay, deadline, authoring validation, and restart-safe release; deployment pending |
| Last synchronized | 2026-09-14 |
- Priority: P1 for operational/financial workflows; P2 generally.
- Owner: schedule schema, scheduler trigger graph, and workflow-builder schedule guidance/validation.
- Observed on: Tectonic USA Day Trading.
- Related: PLAT-041 (durable expected occurrences), PLAT-145 (what happens after a busy collision).
The market-close schedule is configured for 15:55 ET and Daily Pulse for
16:10 ET. The latest close invocation needed about 18m42s for workflow work and
about 22m26s including finalization. Daily Pulse consequently fired while close
still owned the workflow and was recorded skipped_busy.
Moving Pulse by another guessed number of minutes reduces the immediate risk but does not create a correctness guarantee: evaluation, provider latency, or retries can change the close duration again.
The product can express only independent cron times. It cannot express the real relationship: "run Daily Pulse after this market-close occurrence reaches a terminal state." The builder therefore approximates a dependency with a clock offset and the scheduler treats the resulting overlap as an unrelated collision.
Add a dependent schedule trigger rather than a larger magic delay. One schedule may declare:
after_schedule_id = <market-close schedule>
after_terminal_status = completed | any_terminal
delay = 10m
deadline = 17:30 America/New_York
The scheduler should create the dependent occurrence durably when its parent
occurrence becomes terminal. It must use occurrence IDs, not merely schedule
IDs, so yesterday's close cannot release today's Pulse. A failed parent should
follow the declared after_terminal_status policy and remain visible rather
than silently disappearing.
The workflow builder and schedule editor should warn when two independent schedules for the same workflow are closer than the predecessor's recent p95 duration.
Immediate workflow mitigation: move Tectonic Daily Pulse to 16:35–16:45 ET until the dependency contract exists.
- A dependent Daily Pulse occurrence cannot start before the matching close occurrence is terminal.
- The dependency survives restart and releases exactly once.
- Parent failure/cancellation follows an explicit policy and is visible.
- A deadline prevents a stale dependent operation from running the next day.
- Builder validation warns about fixed-time overlaps using observed durations.
Schedules now persist after_schedule_id, after_terminal_status,
after_delay_minutes, and dependency_deadline as typed manifest fields.
The dependency lookup follows the exact durable
scheduled_for -> fire decision -> run_id link for the matching local day; it
does not select whichever run happened to start most recently. Waiting is a
durable pending occurrence and its eventual queue consumption is atomic with
the run lease, so restart cannot release twice or lose the occurrence between
those operations. Failed/stopped parents follow the explicit terminal policy,
and delay/deadline decisions are returned through schedule status rather than
silently skipped.
The builder rejects unknown dependency IDs, self-dependencies, dependency cycles, invalid terminal policies, negative delays, and malformed deadlines. Observed-p95 overlap advice remains a useful authoring enhancement, but is no longer required for correctness because the runtime dependency is authoritative.
The singular after_schedule_id contract now has a canonical list form,
after_schedule_ids. The dependent occurrence waits for all named
prerequisite occurrences on the same local calendar date. The singular field
remains readable for old manifests; replacing the list clears a legacy singular
value unless the caller explicitly supplies both in the same update. Manifest
validation rejects empty/unknown IDs, self-dependencies and cycles across the
full directed graph.
Builder schedule tools, list output, schedule UI details, Pulse Gate, Architecture Review, Technical Review and Fixer guidance expose the fan-in and collision policies. Dependencies still express ordering rather than permission to overlap; the workflow-wide active-execution lease remains in force. Immutable scheduled run folders and the later concurrency boundary are tracked separately by PLAT-320.
A briefly introduced generic schedule wall-clock cap was removed on 2026-09-14 by owner direction. Schedule coordination must not rely on a generic kill switch; cancellation remains explicit, while collision and dependency policies govern when occurrences may start.
Auto-synced from docs/ on main. Edit there, not here.