---
id: STATE-ACC-BAT-002
title: Recurring stream scheduling
object: DOM-ACC-BAT-001
product_area: accounting
module: batches
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
states:
  - name: active
    meaning: Scheduling is enabled for successive collection rolls.
  - name: paused
    meaning: >-
      The scheduler has paused delivery after documented consecutive refusals;
      the stream activity flag can remain true.
  - name: inactive
    meaning: >-
      Scheduled rolling is disabled, while the retained intake batch remains
      available if eligible.
  - name: deleted
    meaning: Stream configuration and its owned scheduling history have been removed.
transitions:
  - from: inactive
    to: active
    trigger: 'Creation enables a stream, or replacement reactivates an inactive one.'
    guard: >-
      Replacement carries the current version; schedule frequency and template
      validation pass.
    side_effects:
      - >-
        Enable the owned schedule, calculate the next occurrence and clear the
        prior pause during reactivation.
      - >-
        Attempt the first batch in the saving transaction; return a planning
        refusal only in the response.
      - >-
        Retire an eligible retained predecessor without preparing its posting
        job.
    who: An administrator with recurring create or update permission
    provenance: test
    source: >-
      services-accounting:modules/batches/tests/integration/services/roll/batch-roller.integration.spec.js#saves
      a recurring batch whose first batch is refused, with the refusal and no
      batch
  - from: active
    to: inactive
    trigger: Replacement turns activity off.
    guard: The supplied version matches the current stream.
    side_effects:
      - >-
        Disable scheduling and remove the next-run answer while retaining the
        current intake collection.
    who: An administrator with recurring update permission
    provenance: test
    source: >-
      services-accounting:modules/batches/tests/e2e/recurring-batches.e2e.spec.js#answers
      no next run once deactivated
  - from: active
    to: paused
    trigger: The scheduler records the fifth consecutive refused roll.
    guard: The documented scheduler refusal threshold is reached.
    side_effects:
      - >-
        Retain the reason on the schedule; stream reads expose it and roll
        preview rejects a paused stream.
    who: The shared scheduling library
    provenance: documented
    source: 'services-accounting:docs/accounting/recurring-batches.md#The roll'
  - from: paused
    to: inactive
    trigger: Replacement deactivates a paused stream.
    guard: The supplied version matches the current stream.
    side_effects:
      - Keep occurrence delivery disabled until later reactivation.
    who: An administrator with recurring update permission
    provenance: observed
    source: >-
      services-accounting:modules/batches/services/persistence/recurring-batches.ts:updateRecurringBatch
  - from: active
    to: deleted
    trigger: The caller deletes the active stream.
    guard: >-
      No lock or routing reference remains, and generated batches are gone apart
      from an unused eligible current batch.
    side_effects:
      - >-
        Remove owned scheduling history and the unused current collection
        together with the stream.
    who: An administrator with recurring delete permission
    provenance: test
    source: >-
      services-accounting:modules/batches/tests/integration/services/roll/batch-roller.integration.spec.js#takes
      its empty active batch with it
  - from: inactive
    to: deleted
    trigger: The caller deletes the inactive stream.
    guard: >-
      No lock or routing reference remains, and generated batches are gone apart
      from an unused eligible current batch.
    side_effects:
      - >-
        Remove owned scheduling history and the unused current collection
        together with the stream.
    who: An administrator with recurring delete permission
    provenance: observed
    source: >-
      services-accounting:modules/batches/services/persistence/recurring-batches.ts
      (header, delete)
  - from: paused
    to: deleted
    trigger: The caller deletes the paused stream.
    guard: >-
      No lock or routing reference remains, and generated batches are gone apart
      from an unused eligible current batch.
    side_effects:
      - >-
        Remove owned scheduling history and the unused current collection
        together with the stream.
    who: An administrator with recurring delete permission
    provenance: observed
    source: >-
      services-accounting:modules/batches/services/persistence/recurring-batches.ts
      (header, delete)
invalid:
  - from: paused
    to: active
    why: >-
      The documented recovery sequence is deactivate then reactivate; an
      unchanged active save is not established to clear a pause.
  - from: deleted
    to: active
    why: >-
      Removed configuration is not reactivated; recreating it is a separate
      record creation.
  - from: inactive
    to: paused
    why: >-
      An inactive schedule does not deliver rolls that could accumulate refusal
      failures.
invariants:
  - INV-DOM-ACC-BAT-001-08
  - INV-DOM-ACC-BAT-001-09
  - INV-DOM-ACC-BAT-001-11
concurrency: >-
  Versioned replacement coordinates configuration edits. The roller locks the
  stream and uses an occurrence key for duplicate delivery protection; that key
  prevents duplicate creation for the same occurrence, not all sequential rolls
  for a stream. Scheduler delivery guarantees remain documented at the imported
  boundary.
evidence:
  - >-
    services-accounting at f47cb263:
    modules/batches/services/persistence/recurring-batches.ts;
    services/roll/first-batch.ts, batch-roller.ts, recurring-batch-roll.ts;
    db/recurring-batches.table.ts; docs/accounting/recurring-batches.md
---

# Recurring Stream Scheduling

## The Lifecycle

```mermaid
stateDiagram-v2
  [*] --> active: enabled creation
  [*] --> inactive: disabled creation
  active --> inactive: disable rolling
  inactive --> active: enable and attempt first batch
  active --> paused: documented refusal threshold
  paused --> inactive: disable before recovery
  active --> deleted: remove
  inactive --> deleted: remove
  paused --> deleted: remove
```

## Transitions Explained

Activity, scheduler pause and intake availability are separate concerns. The transition rows distinguish saving a stream from successfully opening its first collection. A refusal response can accompany a saved active stream, and the current editor does not render that explanation.

## What Cannot Happen

An intake pointer is not an independent stream state. Scheduled rolling changes that pointer without replacing configuration. The invalid rows retain the documented pause-recovery boundary without claiming imported scheduler behavior was executed here.

## Concurrency and Timing

The first-batch and scheduled paths share the roller lock and occurrence protection. Reactivation differs from ordinary scheduled retirement because it does not request predecessor posting; Product has not resolved that policy difference.
