---
id: STATE-ACC-BKS-004
title: Schedule lifecycle
object: DOM-ACC-BKS-006
product_area: accounting
module: books
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
states:
  - name: absent
    meaning: No schedule is stored for this work kind and subject.
  - name: enabled
    meaning: >-
      Enablement and a next occurrence make the schedule eligible for claiming
      when due.
  - name: disabled
    meaning: The owner has disabled new claims and the next occurrence is empty.
  - name: paused
    meaning: >-
      A pause reason accompanies disabled scheduling after repeated failure or
      invalid stored recurrence.
  - name: ended
    meaning: The recurrence has no future occurrence; history remains stored.
  - name: deleted
    meaning: The schedule row and its cascaded run history have been removed.
transitions:
  - from: absent
    to: enabled
    trigger: Save an enabled schedule
    guard: >-
      Recurrence and the owner’s minimum interval are valid, with a default
      workspace zone if needed
    side_effects:
      - Compute the next occurrence from the current time
    who: The owning module within its save transaction
    provenance: observed
    source: >-
      services-accounting:modules/maintenance/services/persistence/schedules.ts:createOwnedSchedule
  - from: absent
    to: disabled
    trigger: Save a disabled schedule
    guard: Definition and interval checks also apply to disabled saves
    side_effects:
      - Store no next occurrence
    who: The owning module
    provenance: test
    source: >-
      services-accounting:modules/maintenance/tests/integration/services/scheduling/schedules-end-to-end.integration.spec.js#has
      no next run (the premise)
  - from: enabled
    to: enabled
    trigger: Claim work or save a changed definition
    guard: >-
      A claim requires due enabled timing; an owner change requires valid
      recurrence
    side_effects:
      - Allocate one run identity for the selected occurrence
      - Recompute future timing; claims can skip older missed occurrences
      - Update failure counts when run outcomes are recorded
    who: 'Dispatcher, consumer or owning module'
    provenance: test
    source: >-
      services-accounting:modules/maintenance/tests/integration/services/scheduling/schedules-end-to-end.integration.spec.js#runs
      next at 06:15 the following morning after it
  - from: enabled
    to: paused
    trigger: Pause after the fifth recorded failure
    guard: The schedule remains enabled when its failure count reaches the allowance
    side_effects:
      - >-
        Disable future claims, clear next timing and record the latest failure
        summary
    who: Schedule consumer
    provenance: test
    source: >-
      services-accounting:modules/maintenance/tests/integration/services/scheduling/schedules-end-to-end.integration.spec.js#pauses
      the schedule after the fifth failure in a row
  - from: enabled
    to: disabled
    trigger: Disable future scheduling
    guard: The owner saves disabled enablement
    side_effects:
      - Clear the next occurrence without cancelling an already-claimed run
    who: The owning module
    provenance: test
    source: >-
      services-accounting:modules/maintenance/tests/integration/services/scheduling/schedules-end-to-end.integration.spec.js#is
      never claimed
  - from: disabled
    to: enabled
    trigger: Enable a disabled schedule
    guard: The saved definition remains valid
    side_effects:
      - Compute future timing from now without replaying missed work
    who: The owning module
    provenance: inferred
    source: >-
      services-accounting:modules/maintenance/services/persistence/schedules.ts:updateOwnedSchedule
    note: >-
      A save recomputes future timing from the current time, so enabling does
      not replay the missed occurrence sequence.
  - from: paused
    to: enabled
    trigger: Enable a paused schedule
    guard: The recurrence passes validation
    side_effects:
      - Compute the next occurrence from now
      - Clear the pause reason and consecutive failure count
    who: The owning module after an administrator’s change
    provenance: inferred
    source: >-
      services-accounting:modules/maintenance/services/persistence/schedules.ts:updateOwnedSchedule
    note: >-
      The shared repository clears pause details and failure count on
      re-enablement and computes a fresh next occurrence.
  - from: enabled
    to: ended
    trigger: Exhaust the recurrence
    guard: There is no next occurrence after the claim
    side_effects:
      - Leave next timing empty and retain history
    who: Schedule dispatcher
    provenance: inferred
    source: >-
      services-accounting:modules/maintenance/services/persistence/schedules.ts:ScheduleSummary
    note: >-
      An enabled recurrence with no next instant retains its row but has no due
      time for a future dispatcher claim.
  - from: enabled
    to: deleted
    trigger: Remove an enabled schedule
    guard: The owner removes its schedule or workspace purge reaches it
    side_effects:
      - Cascade the schedule’s run history
    who: Owning module or maintenance purge
    provenance: observed
    source: >-
      services-accounting:modules/maintenance/services/persistence/schedules.ts:deleteSchedule
  - from: paused
    to: deleted
    trigger: Remove a paused schedule
    guard: The owner removes its schedule or workspace purge reaches it
    side_effects:
      - Cascade the schedule’s run history
    who: Owning module or maintenance purge
    provenance: observed
    source: >-
      services-accounting:modules/maintenance/services/persistence/schedules.ts:deleteSchedule
  - from: disabled
    to: deleted
    trigger: Remove a disabled schedule
    guard: The owner removes its schedule or workspace purge reaches it
    side_effects:
      - Cascade the schedule’s run history
    who: Owning module or maintenance purge
    provenance: observed
    source: >-
      services-accounting:modules/maintenance/services/persistence/schedules.ts:deleteSchedule
  - from: ended
    to: deleted
    trigger: Remove an ended schedule
    guard: The owner removes its schedule or workspace purge reaches it
    side_effects:
      - Cascade the schedule’s run history
    who: Owning module or maintenance purge
    provenance: observed
    source: >-
      services-accounting:modules/maintenance/services/persistence/schedules.ts:deleteSchedule
  - from: enabled
    to: paused
    trigger: Pause an invalid saved definition
    guard: The dispatcher cannot prepare the stored recurrence
    side_effects:
      - Disable timing and retain an invalid-definition pause reason
    who: Schedule dispatcher
    provenance: observed
    source: >-
      services-accounting:modules/maintenance/aws/lambda/DISPATCHER.ts:handler;
      services-accounting:package-lock.json
invalid:
  - from: disabled
    to: paused
    why: >-
      Disabling prevents new claims, but earlier claimed work can still fail;
      disabled failure does not trigger another automatic pause.
  - from: deleted
    to: enabled
    why: >-
      A removed schedule can be replaced only by a newly created record with
      fresh history.
  - from: absent
    to: enabled
    why: >-
      Creation refuses malformed recurrence or an interval below the owner’s
      minimum.
concurrency: >-
  Claims skip schedule rows locked elsewhere, and the deployed dispatcher runs
  one invocation at a time. Occurrence uniqueness protects the run identity, not
  handler execution: queued and running runs may restart. The consumer cap
  permits two concurrent invocations and does not serialize duplicate messages
  for one run.
invariants:
  - INV-DOM-ACC-BKS-006-05
  - INV-DOM-ACC-BKS-006-06
evidence:
  - >-
    services-accounting at f47cb263: modules/maintenance/db/schedules.table.ts;
    services/persistence/schedules.ts; aws/lambda/DISPATCHER.ts, CONSUMER.ts;
    aws/logic/schedule-workers.lambda.ts
  - >-
    services-accounting at f47cb263:
    modules/maintenance/tests/integration/services/scheduling/schedules-end-to-end.integration.spec.js
---

# Schedule lifecycle

## The Lifecycle

```mermaid
stateDiagram-v2
  state "absent" as s0
  state "enabled" as s1
  state "disabled" as s2
  state "paused" as s3
  state "ended" as s4
  state "deleted" as s5
  s0 --> s1: Save an enabled schedule
  s0 --> s2: Save a disabled schedule
  s1 --> s1: Claim work or save a changed definition
  s1 --> s3: Pause after the fifth recorded failure
  s1 --> s2: Disable future scheduling
  s2 --> s1: Enable a disabled schedule
  s3 --> s1: Enable a paused schedule
  s1 --> s4: Exhaust the recurrence
  s1 --> s5: Remove an enabled schedule
  s3 --> s5: Remove a paused schedule
  s2 --> s5: Remove a disabled schedule
  s4 --> s5: Remove an ended schedule
  s1 --> s3: Pause an invalid saved definition
```

## Transitions Explained

The subject’s module saves recurrence in its own record transaction. Definition changes recompute future timing, and enabling a paused schedule clears its pause and failure count. The workspace zone fills an unspecified time zone.

Claims choose a due occurrence and move timing forward. The history row can have multiple execution attempts. Success resets consecutive failure, failure increases it and skipped work leaves the count unchanged. Five failures pause an enabled schedule; the dispatcher can also pause an invalid saved definition.

## What Cannot Happen

Disabled and paused schedules receive no new claims. They do not resume without an owner save, but existing claimed runs are not cancelled by disablement. Deletion removes history through cascade; no database guarantee establishes that a schedule is removed whenever every possible owner is removed.

## Concurrency and Timing

A minute rule drives bounded claim passes. Missed recurrence can be skipped rather than replayed individually, and the two-invocation consumer cap lets excess messages wait. The tested spring gap shifts a scheduled early-morning occurrence forward by an hour. Pausing is visible in owner summaries and history, with no examined attention notice.
