Ex4pm.Evidence.Batch (ex4pm v26.9.9)

Copy Markdown View Source

Durable, resumable batch ledger (hand-written; classification: pat-wrapper-over-admitted-data per docs/PRD-v26.9.10.md R2, matching beam4pm's own BeamPM.Contracts precedent).

Models exactly the shape the RCA specified as missing: a batch of admitted/qualified/published/merged work counted against a target, carrying a coded Ex4pm.Standing.Coded standing.

ex4pm itself does not persist this anywhere -- no database, no file store beyond what Ex4pm.Evidence.Store already does for receipts. This is a data-shape + transition-logic contract, not a new storage subsystem; persistence is the caller's job.

Summary

Functions

Bumps :admitted/:qualified/:published/:merged counters by the delta amounts given in delta, and updates :next_cell when provided. Unspecified keys are left untouched.

Closes the batch: ALIVE[<id>_50]-shaped coded standing when quota is met, else BUILD_BROKEN[TAKT_SHORTFALL].

Starts a new batch at zero counters, no standing set (still open/unclosed).

True when qualified work has met or exceeded the batch's target.

RCA rule 2: "if active batch exists, NEVER start another batch, continue it." A batch whose own standing is already closed (:alive or :build_broken) must not be silently re-opened -- resuming it is prohibited. Any other batch (no standing yet, or a non-closed standing) may be continued.

Types

t()

@type t() :: %Ex4pm.Evidence.Batch{
  admitted: non_neg_integer(),
  base: String.t(),
  id: String.t(),
  merged: non_neg_integer(),
  next_cell: String.t() | nil,
  published: non_neg_integer(),
  qualified: non_neg_integer(),
  standing: Ex4pm.Standing.Coded.t() | nil,
  target: non_neg_integer()
}

Functions

advance(batch, delta)

@spec advance(t(), map()) :: t()

Bumps :admitted/:qualified/:published/:merged counters by the delta amounts given in delta, and updates :next_cell when provided. Unspecified keys are left untouched.

close(batch)

@spec close(t()) :: t()

Closes the batch: ALIVE[<id>_50]-shaped coded standing when quota is met, else BUILD_BROKEN[TAKT_SHORTFALL].

new(id, target)

@spec new(String.t(), non_neg_integer()) :: t()

Starts a new batch at zero counters, no standing set (still open/unclosed).

quota_met?(batch)

@spec quota_met?(t()) :: boolean()

True when qualified work has met or exceeded the batch's target.

resume(batch)

@spec resume(t()) :: {:continue, t()} | {:prohibited, String.t()}

RCA rule 2: "if active batch exists, NEVER start another batch, continue it." A batch whose own standing is already closed (:alive or :build_broken) must not be silently re-opened -- resuming it is prohibited. Any other batch (no standing yet, or a non-closed standing) may be continued.