---
id: DOM-ACC-CLS-003
title: Close Run
realises: CON-ACC-CLS-003
product_area: accounting
module: close
revision: 1
status: draft
design_intent: confirmed
origin: >-
  services-accounting:docs/milestones/plans/ACCT-M4-plan.md#4a rows 74 and 75
  (class A: close runs as a job with six steps, one active run per period, the
  reopen with its reason; plan approved 2026-10-21, D12)
implementation_evidence: partially-observed
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
owned_by: SVC-ACC-CLS-fiscal-close
readable_by: []
lifecycle: STATE-ACC-CLS-002
custom_fields: none
identity: >-
  Acceptance assigns a time-ordered run identifier within the workspace. A
  supplied command key locates the same request on replay. Each execution
  attempt has a separate job, and the run references its latest job.
attributes:
  - name: id
    kind: text
    required: true
    computed: true
    notes: Acceptance generates the run’s time-ordered identifier.
  - name: kind
    kind: enum
    required: true
    computed: true
    notes: >-
      The request becomes a period close, a final-month year close or a reopen;
      the database freezes that kind.
  - name: 'business unit, fiscal year and period number'
    kind: reference
    required: true
    computed: false
    notes: >-
      The database retains the requested unit, year and month unchanged and
      enforces the unit-to-year relationship.
  - name: status
    kind: enum
    required: true
    computed: false
    notes: 'The run records queued, running, failed, succeeded or cancelled.'
  - name: command key
    kind: text
    required: true
    computed: false
    notes: >-
      HTTP submission requires a trimmed key between one and 255 characters.
      Persistence permits a null key; supplied keys are workspace-unique, frozen
      and withheld from export.
  - name: note or reason
    kind: text
    required: false
    computed: false
    notes: >-
      Closing accepts an optional note; reopening requires a reason. The API and
      lock task enforce the ten-character minimum, the database enforces
      presence and the upper limit, and no exposed operation edits the note. The
      write-once trigger does not include it.
  - name: acknowledged warnings
    kind: collection
    required: true
    computed: false
    notes: >-
      The request stores up to fifty deduplicated warning references. They
      remain fixed and do not determine whether execution may close.
  - name: requested by and requested at
    kind: reference
    required: true
    computed: true
    notes: The run preserves the submission caller and request time.
  - name: finished at
    kind: date
    required: false
    computed: true
    notes: >-
      Failed, succeeded and cancelled runs have a finish time at or after their
      request; retry clears it while the run is running again.
  - name: close reversed
    kind: reference
    required: false
    computed: true
    notes: >-
      A reopen may link to its original close; a period closed before run
      recording can leave that link absent.
  - name: binding readiness
    kind: collection
    required: false
    computed: true
    notes: >-
      Revalidation stores the close’s evaluated checklist under the period lock.
      It is absent before evaluation and on normal reopen runs, and its stored
      size is below 512 kilobytes.
  - name: step outputs
    kind: collection
    required: true
    computed: true
    notes: >-
      Outputs retain committed step results: lock details, readiness outcome,
      recognition or reversal, skipped work, aging request, generated years and
      failure details. The database limits the document to below 512 kilobytes.
  - name: current job and attempts
    kind: collection
    required: true
    computed: false
    notes: >-
      The first execution and retries have separate jobs with steps, attempts
      and events. The view reads at most the oldest one hundred jobs.
  - name: version
    kind: number
    required: true
    computed: true
    notes: Every persistence update advances the record version.
relationships:
  - to: DOM-ACC-CLS-001
    cardinality: many runs to one period
    ownership: >-
      The run owns the period reference, which prevents deleting that period
      first.
    cross_service: false
  - to: DOM-ACC-CLS-002
    cardinality: a close run holds at most one binding evaluation
    ownership: The run retains the calculation its close task performed.
    cross_service: false
  - to: DOM-ACC-CLS-004
    cardinality: 'one snapshot for each successful close, and none for other requests'
    ownership: >-
      The snapshot references its run and must be removed before the run can be
      deleted.
    cross_service: false
  - to: DOM-ACC-CLS-003
    cardinality: one prior close per reopen; multiple reopens may refer to that close
    ownership: >-
      The reopen owns an optional close backlink; both records are purged
      without a restricting foreign key on that link.
    cross_service: false
invariants:
  - id: INV-DOM-ACC-CLS-003-01
    statement: >-
      The database freezes kind, period identity, command key, requester,
      request time, warning acknowledgements and the reversed-close reference.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/db/fiscal-close.table.integration.spec.js#refuses
      changing a run’s kind, which is written once
  - id: INV-DOM-ACC-CLS-003-02
    statement: >-
      The database allows at most one queued or running run per period, and the
      service refuses a new request while a failed run retains a transitional
      month.
    enforced_at:
      - database
      - service
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/jobs/close-job.integration.spec.js#refuses
      another key while the first run holds the period, as period-run-active
  - id: INV-DOM-ACC-CLS-003-03
    statement: >-
      Service updates enforce the run-transition table and expected status;
      database checks require a valid finish time exactly for finished states.
    enforced_at:
      - database
      - service
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/persistence/period-close-runs.integration.spec.js#refuses
      a move from a status the run is no longer in, telling the writer it
      changed
  - id: INV-DOM-ACC-CLS-003-04
    statement: >-
      Reopen submission and its lock task reject reasons shorter than ten
      characters.
    enforced_at:
      - api
      - service
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/jobs/reopen-job.integration.spec.js#refuses
      a reason under ten characters in the job’s lock, the period left closed
  - id: INV-DOM-ACC-CLS-003-05
    statement: >-
      A supplied command key is unique within the workspace; identical kind,
      period, note and acknowledgement content returns its existing run, while
      differing content is refused.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/jobs/close-job.integration.spec.js#refuses
      the same key with another body as idempotency-key-reused (409)
  - id: INV-DOM-ACC-CLS-003-06
    statement: A run may link to an earlier close only when its kind is reopen.
    enforced_at:
      - database
    provenance: observed
    source: >-
      services-accounting:modules/fiscal-close/db/period-close-runs.table.ts:periodCloseRuns
      (period_close_runs_close_run_check)
  - id: INV-DOM-ACC-CLS-003-07
    statement: >-
      Recognition and reversal commit financial work with their run output;
      replay reuses that output, while job events and roll-forward commit
      separately.
    enforced_at:
      - service
      - database
    provenance: observed
    source: >-
      services-accounting:modules/fiscal-close/services/jobs/fiscal-close-job-tasks.ts:recognizeRevenue
  - id: INV-DOM-ACC-CLS-003-08
    statement: >-
      Retry creates a linked job, and failure handling checks that the reported
      job is the run’s current job before waiting on its period lock.
    enforced_at:
      - service
    provenance: observed
    source: >-
      services-accounting:modules/fiscal-close/services/jobs/fiscal-close-jobs.ts:applyRunFailure
  - id: INV-DOM-ACC-CLS-003-09
    statement: >-
      Run queries and changes use workspace isolation; job roots carry workspace
      and customer context and are exposed through the run.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/db/fiscal-close.table.integration.spec.js#hides
      the run and the snapshot from another tenant
  - id: INV-DOM-ACC-CLS-003-10
    statement: >-
      The API and database cap notes at two thousand characters and warning
      acknowledgements at fifty.
    enforced_at:
      - api
      - database
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/db/fiscal-close.table.integration.spec.js#refuses
      a note longer than 2000 characters
  - id: INV-DOM-ACC-CLS-003-11
    statement: >-
      Retry and cancellation require the close grant for close runs and the
      reopen grant for reopen runs.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/e2e/fiscal-close-runs.e2e.spec.js#refuses
      a reader’s retry with 403: the handler requires the run’s kind’s key
physical:
  - period_close_runs
  - fiscal_period_close_jobs
  - fiscal_period_close_job_steps
  - fiscal_period_close_job_events
  - fiscal_period_reopen_jobs
  - fiscal_period_reopen_job_steps
  - fiscal_period_reopen_job_events
evidence:
  - >-
    services-accounting at f47cb263:
    modules/fiscal-close/db/period-close-runs.table.ts,
    fiscal-close-jobs.table.ts; services/persistence/period-close-runs.ts,
    fiscal-close-jobs.ts; services/jobs/*.ts; services/runs/close-run-view.ts;
    services/rules/transitions.ts;
    db/migrations/0064_fiscal_close_foundation.sql, 0065_fiscal_close_jobs.sql;
    db/row-level-security.ts (job roots).
  - >-
    react-console at baf6dae3: components/accounting/close/close-dialog.tsx,
    reopen-dialog.tsx, close-run-view.tsx, close-history.tsx.
custom_field_declaration:
  provenance: unknown
  source: FND-ACC-CLS-053
---

# Close Run

## What It Models

A run records a finance action and its outcome. A durable job carries out that action, and each retry adds an execution attempt to the same run. Staff can therefore follow one request through multiple jobs without treating recovery as another close.

## Identity and Attributes

The database freezes the principal request identifiers, caller, time and acknowledgements. The note participates in replay comparison but is not frozen by the write-once trigger. Runtime status, readiness and outputs change as work proceeds. Outputs preserve domain results; job steps and events describe execution. Recognition and reversal use the run’s recorded output to avoid repeating committed financial work.

## Relationships

Each run targets one period. Successful closes own one immutable snapshot; normal reopen runs own none. A reopen may identify the earlier close whose revenue it reverses. Jobs reference the run and must be deleted before it.

## Invariants

The invariant rows distinguish database constraints, service transitions and task behavior. They do not guarantee that every task event commits with its domain work. Calendar generation is also outside that atomic boundary. Current-job checks in failure handling occur before the period lock, leaving a concurrency concern for recovery.

## How It Is Stored and Shared

Run rows are workspace-scoped. Job roots retain workspace and customer context for cross-workspace execution infrastructure; their children belong to the job. Service operations assemble runs and execution history for the console. Customer exports include run data without command keys and omit operational job tables.

## Open Design Questions

Replay must include requester identity to satisfy the recorded product direction. Recovery needs a response that tells the console whether retry or cancellation is allowed. Failure races, missing history events and long histories need explicit proof or correction.
