Optionalrecords: Map<string, ApprovalRecord>Insert — unless an OPEN request (pending | claimed | escalated) already exists for the same (workflowId, runId, stepKey), or any record already carries the same captured (stepPath, suspendedAt, resumeCount) fingerprint. The existing record is returned with created: false, so a stale reconciler cannot file over a decision that landed after its history read. A later re-suspension changes the fingerprint and opens fresh.
Ordered per filter.orderBy: oldest-first (createdAt, then id — FIFO
queue order) by default, or the reviewer queue order (priority → SLA →
FIFO, byReviewerOrder) under 'reviewer' — applied BEFORE filter.limit,
so a bounded page is the top of the reviewer queue. Bounded by
filter.limit/filter.after; a tenant-bound bare list() with no limit
defaults to MAX_APPROVAL_LIST_LIMIT so repeated dashboard polls cannot
trigger unbounded full-table scans. Page complete history with an
explicit after cursor. The cron-only SystemApprovalStore view stays
complete (no default) for reconciliation and the SLA sweep.
Aggregate queue metrics for this store's tenant — field semantics on
ApprovalMetrics (types.ts), computed here instead of requiring callers
to load every record into JavaScript. nowMs is the SLA-breach
reference instant (the service's injected clock), keeping the
computation deterministic under tests.
Compare-and-swap: apply patch iff the current status is in from.
Returns the updated record, or null when the guard fails (the status
moved concurrently, or the id is unknown — callers disambiguate via
get()).
In-memory reference implementation, bound to one tenant at construction. Mirrors D1ApprovalStore semantics — both run the same contract test suite, including the cross-tenant cases over ONE shared backend (pass the same
recordsMap to two instances; the InMemoryApprovalStoreFactory does exactly that). CAS atomicity holds because the check-and-mutate section is synchronous (no awaits inside).Every read/write carries the tenant predicate sourced from the constructor field — never from a parameter — and create() STAMPS the tenant, so no caller (and no spoofed input) can write another tenant's row.