Skip to content

Commit 839ba64

Browse files
Describe live schedule audit visibility surfaces
Replace the Visibility section's "tracked as follow-up work" copy with links to the surfaces that now expose the per-schedule audit stream across the wire: Waterline HTTP + UI, the standalone server HTTP endpoint, the dw schedule:history CLI command, and the Python SDK Client.get_schedule_history / Client.iter_schedule_history plus the matching ScheduleHandle convenience methods. The shared pagination contract (limit 1-500, after_sequence cursor) and namespace scoping are called out once so operators understand the surfaces share one source of truth. Document migration behavior for schedules that predate the workflow_schedule_history_events migration: no retroactive ScheduleCreated is backfilled, audit rows accumulate from the next recorded lifecycle transition onward, sequence numbers still start at 1 per schedule, and no operator action is required to opt a schedule into the stream.
1 parent 9aa11e0 commit 839ba64

1 file changed

Lines changed: 70 additions & 7 deletions

File tree

docs/features/schedules.md

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -388,13 +388,76 @@ built-in TTL for audit events.
388388

389389
### Visibility
390390

391-
Today the audit stream is queryable in-process through the
392-
`WorkflowSchedule::historyEvents()` Eloquent relation
393-
(`ConfiguredV2Models::query('schedule_history_event_model', ...)`).
394-
Standalone server HTTP endpoints, Waterline UI surfaces, and CLI/SDK
395-
helpers that expose the audit stream across the wire are tracked as
396-
follow-up work; the persisted events are already stable to consume from
397-
the database directly.
391+
The audit stream is exposed through every operator surface:
392+
393+
- **In-process (Eloquent).** `WorkflowSchedule::historyEvents()`
394+
(resolved through `ConfiguredV2Models::query(
395+
'schedule_history_event_model', ...)`) returns the stream for a
396+
schedule from within the host application.
397+
- **Waterline HTTP.**
398+
`GET /waterline/api/v2/schedules/{scheduleId}/history` returns the
399+
stream with `limit` (1–500, default 100) and `after_sequence`
400+
cursor pagination. The response is scoped to the Waterline
401+
namespace so multi-tenant deployments only see their tenant's
402+
audit rows.
403+
- **Waterline UI.** The **History** action on each row in the
404+
Waterline schedule registry opens a modal that renders the stream
405+
for that schedule. Events are shown with their sequence, recorded
406+
timestamp, event type, linked workflow instance and run IDs, and
407+
formatted payload, with a **Load more** control that advances the
408+
cursor in batches of 100.
409+
- **Standalone server HTTP.**
410+
`GET /api/schedules/{scheduleId}/history` on the standalone
411+
server returns the same stream with the same pagination contract
412+
and the same `X-Namespace` scoping rules. History remains
413+
available after a schedule is soft-deleted, since the audit trail
414+
is what operators reach for to reconstruct a removed schedule.
415+
- **CLI.** `dw schedule:history <schedule-id>` prints the stream
416+
as a table (`Seq`, `Event`, `Recorded At`, `Workflow Refs`) with
417+
a **More events available** hint when `has_more` is true.
418+
`--limit` and `--after-sequence` forward to the server endpoint,
419+
`--all` pages through every remaining event, and
420+
`--output=json` / `--output=jsonl` emit structured output
421+
(`jsonl` drops the cursor envelope so each line is a
422+
self-contained event).
423+
- **Python SDK.** `Client.get_schedule_history(schedule_id, *, limit,
424+
after_sequence)` returns a single `ScheduleHistoryPage`, and
425+
`Client.iter_schedule_history(schedule_id, *, page_size)` is an
426+
`AsyncIterator[ScheduleHistoryEvent]` that pages through the full
427+
stream. `ScheduleHandle` exposes matching `.history(...)` and
428+
`.iter_history(...)` convenience methods.
429+
430+
All of these surfaces read from the same `workflow_schedule_history_events`
431+
table; the payload-key contract in
432+
[Payload contract stability](#payload-contract-stability) applies
433+
regardless of which surface the operator uses.
434+
435+
### Migration behavior for pre-audit schedules
436+
437+
The schedule audit stream was introduced together with the
438+
`workflow_schedule_history_events` table. Schedules that were
439+
created before that migration ran have no retroactive
440+
`ScheduleCreated` event — `ScheduleManager` records audit events
441+
only as lifecycle transitions happen, and the migration does not
442+
synthesize a backfilled event for existing schedules.
443+
444+
What this means for operators:
445+
446+
- A pre-existing schedule's stream is empty until its next
447+
lifecycle transition. Pausing, resuming, updating, triggering,
448+
skipping a trigger, or deleting the schedule appends events as
449+
normal from that point on.
450+
- Sequence numbers still start at `1` and increment monotonically
451+
per schedule. A pre-existing schedule whose first recorded event
452+
is a `SchedulePaused` will have `sequence = 1` on that row; the
453+
absence of a preceding `ScheduleCreated` is expected.
454+
- `ScheduleTriggered` and `ScheduleTriggerSkipped` events are
455+
written every time a tick evaluates the schedule, so any schedule
456+
that fires on a cron cadence after the migration will accumulate
457+
audit rows without operator intervention.
458+
- No operator action is required to opt a schedule into the audit
459+
stream. The stream is always on; it simply has no rows until the
460+
first post-migration event is written.
398461

399462
## Skip tracking
400463

0 commit comments

Comments
 (0)