---
id: CAP-ACC-CLS-008
title: Complete a guarded month-end close
feature: FEAT-ACC-CLS-03
product_area: accounting
module: close
revision: 1
status: draft
product_intent: confirmed
implementation_evidence: partially-observed
supersedes:
  - >-
    CAP-ACC-REC-017 (react-console acceptance inventory, the close request and
    job)
  - RUL-ACC-REC-076
  - RUL-ACC-REC-077
  - RUL-ACC-REC-078
  - RUL-ACC-REC-079
  - RUL-ACC-REC-080
  - RUL-ACC-REC-081
  - RUL-ACC-REC-083
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - >-
    services-accounting:docs/milestones/plans/ACCT-M4-plan.md#4a row 74 (class
    A: close runs as a job with six steps, one active run per period)
  - >-
    services-accounting:docs/milestones/ACCT-M4-contracts-and-events-for-commerce.md#Rulings
    recorded during the milestone
purpose: >-
  A controller submits one close request whose durable execution validates the
  books and records the result.
outcomes:
  - >-
    Repeated requests resolve to the original run when their command content
    matches.
  - Required blockers discovered during execution leave the month open.
  - Completed closes retain the requester and the evidence evaluated by the run.
concepts:
  - CON-ACC-CLS-003
  - CON-ACC-CLS-001
actors:
  - ACT-ACC-015
  - ACT-ACC-016
surfaces:
  - console
  - api
  - job
preconditions:
  - The selected period is open and next in the closing sequence.
postconditions:
  - >-
    The run completes with retained evidence, releases the period after refusal,
    or retains it for recovery after failure.
permissions:
  - key: accounting.fiscal-periods.close
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-ACC-CLS-008-01
    proved_by: [ATS-ACC-CLS-008-01, ATS-ACC-CLS-008-10, TST-ACC-CLS-005, TST-ACC-CLS-017]
    statement: >-
      A close request requires a trimmed key of one to 255 characters and
      accepts an optional note up to two thousand characters and at most fifty
      warning acknowledgements; invalid or additional fields are refused.
    kind: validation
    enforced_at:
      - api
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/e2e/fiscal-close-runs.e2e.spec.js#refuses
      a close without an idempotency key with 400
    origin: none
  - id: BR-CAP-ACC-CLS-008-02
    proved_by: [ATS-ACC-CLS-008-01, TST-ACC-CLS-017]
    statement: The close permission is required to submit a close request.
    kind: permission
    enforced_at:
      - api
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/e2e/fiscal-close-runs.e2e.spec.js#refuses
      a principal without accounting.fiscal-periods.close with 403
    origin: >-
      services-accounting:docs/milestones/plans/ACCT-M4-plan.md#4a row 73 (class
      A: authorised)
  - id: BR-CAP-ACC-CLS-008-03
    proved_by: [ATS-ACC-CLS-008-02, TST-ACC-CLS-017, TST-ACC-CLS-018, TST-ACC-CLS-021, TST-ACC-CLS-029]
    statement: >-
      Repeating a key with the same command returns its run without another job,
      while a changed period, note or acknowledgement set is refused.
    kind: policy
    enforced_at:
      - service
      - database
    must_prove: true
    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)
    origin: none
  - id: BR-CAP-ACC-CLS-008-04
    proved_by: [ATS-ACC-CLS-008-03]
    statement: >-
      The product should refuse another person’s command under an idempotency
      key already used by a requester.
    kind: policy
    enforced_at: []
    must_prove: true
    provenance: documented
    source: >-
      services-accounting:docs/milestones/ACCT-M4-contracts-and-events-for-commerce.md#Rulings
      recorded during the milestone
    origin: >-
      services-accounting:docs/milestones/ACCT-M4-contracts-and-events-for-commerce.md#Rulings
      recorded during the milestone
    examples:
      negative: >-
        A colleague’s matching command currently receives the original
        requester’s run.
  - id: BR-CAP-ACC-CLS-008-05
    proved_by: [ATS-ACC-CLS-008-02, ATS-ACC-CLS-008-09, TST-ACC-CLS-017, TST-ACC-CLS-018, TST-ACC-CLS-021, TST-ACC-CLS-029]
    statement: >-
      A period accepts no competing run while one is queued or running, or while
      a failed run retains it in closing or reopening state.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    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
    origin: >-
      services-accounting:docs/milestones/plans/ACCT-M4-plan.md#4a row 74 (class
      A: one active run per period)
  - id: BR-CAP-ACC-CLS-008-06
    proved_by: [ATS-ACC-CLS-008-04, TST-ACC-CLS-004, TST-ACC-CLS-021]
    statement: >-
      The request rejects an unavailable period or invalid sequence immediately,
      but accepts incomplete readiness for the job to assess.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/jobs/close-job.integration.spec.js#ends
      the run failed at revalidate, not retryable
    origin: none
  - id: BR-CAP-ACC-CLS-008-07
    proved_by: [ATS-ACC-CLS-008-05, TST-ACC-CLS-017, TST-ACC-CLS-021]
    statement: >-
      The final period of a fiscal year creates a year-close run; other periods
      create period-close runs.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/jobs/close-job.integration.spec.js#runs
      it as a year close, succeeded
    origin: none
  - id: BR-CAP-ACC-CLS-008-08
    proved_by: [ATS-ACC-CLS-008-05, ATS-ACC-CLS-008-09, TST-ACC-CLS-017, TST-ACC-CLS-021]
    statement: >-
      A close job locks, revalidates, recognizes revenue, skips cost of goods
      sold, finalizes and rolls years forward, starting after the request
      transaction commits.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/e2e/fiscal-close-runs.e2e.spec.js#reads
      the run succeeded with its six steps
    origin: >-
      services-accounting:docs/milestones/plans/ACCT-M4-plan.md#4a row 74 (class
      A: close runs as a job with six steps)
  - id: BR-CAP-ACC-CLS-008-09
    proved_by: [ATS-ACC-CLS-008-06, ATS-ACC-CLS-008-09, TST-ACC-CLS-023]
    statement: >-
      Steps through finalization lock the period and reuse committed results on
      replay; roll-forward executes separately after closure.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: observed
    source: >-
      services-accounting:modules/fiscal-close/services/jobs/fiscal-close-job-tasks.ts:rollForward
    origin: fiscal-close design L9 (Rhythm 1.0 CLOSE-9 fixed)
  - id: BR-CAP-ACC-CLS-008-10
    proved_by: [ATS-ACC-CLS-006-08, ATS-ACC-CLS-008-04, TST-ACC-CLS-004, TST-ACC-CLS-010, TST-ACC-CLS-021]
    statement: >-
      Revalidation stores readiness and refuses required blocked, pending or
      error checks, releasing the month and failing the run without retry.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/jobs/close-job.integration.spec.js#returns
      the period to open with no run holding it
    origin: >-
      services-accounting:docs/milestones/plans/ACCT-M4-plan.md#4a row 74 (class
      A; plan approved 2026-10-21)
  - id: BR-CAP-ACC-CLS-008-11
    proved_by: [ATS-ACC-CLS-008-07]
    statement: >-
      Unacknowledged warning outcomes do not stop execution and are recorded in
      a run event.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-accounting:modules/fiscal-close/services/jobs/fiscal-close-job-tasks.ts:revalidate
    origin: none
  - id: BR-CAP-ACC-CLS-008-12
    proved_by: [ATS-ACC-CLS-008-08, TST-ACC-CLS-001]
    statement: >-
      The console reviews warning outcomes and requires their acknowledgement
      before asking for the exact month label as confirmation.
    kind: policy
    enforced_at:
      - console
    must_prove: true
    provenance: observed
    source: 'react-console:components/accounting/close/close-dialog.tsx:CloseDialog'
    origin: none
  - id: BR-CAP-ACC-CLS-008-13
    proved_by: [ATS-ACC-CLS-008-05, ATS-ACC-CLS-008-09, TST-ACC-CLS-017, TST-ACC-CLS-021]
    statement: >-
      Finalization atomically records the closed period, requester and close
      time, close snapshot, aging request and successful run, with the year
      following its final month.
    kind: lifecycle
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/jobs/close-job.integration.spec.js#closes
      the period, by the requester, naming the run
    origin: >-
      services-accounting:docs/milestones/plans/ACCT-M4-plan.md#4a row 74 (class
      A; plan approved 2026-10-21)
  - id: BR-CAP-ACC-CLS-008-14
    proved_by: []
    statement: >-
      Job authorization uses the system caller’s grants; the requester’s close
      grant is checked at submission.
    kind: permission
    enforced_at:
      - service
    must_prove: false
    provenance: documented
    source: >-
      services-accounting:docs/migrations/fiscal-close/README.md#Decisions and
      departures from the brief
    origin: none
scenarios:
  - id: SC-CAP-ACC-CLS-008-01
    given: September is clean and August is closed.
    when: The controller confirms September in the console with no warnings.
    then: >-
      Six steps settle, the empty recognition and cost steps are skipped, and
      the completed month has a close record.
    provenance: test
    source: >-
      react-console:acceptance/accounting/playwright/journeys/reconciliation-close/ACT-ACC-0613.spec.ts#ACT-ACC-0613
  - id: SC-CAP-ACC-CLS-008-02
    given: September has an unposted batch and a close request is submitted directly.
    when: The job evaluates readiness.
    then: >-
      Revalidation fails, returns September to open and identifies the batch
      blocker.
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/jobs/close-job.integration.spec.js#names
      the blocking check in the run’s failure
  - id: SC-CAP-ACC-CLS-008-03
    given: A command has a saved run and an unchanged request body.
    when: The same keyed command is submitted again.
    then: The response returns that run without another job.
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/jobs/close-job.integration.spec.js#answers
      the same run for the same key, created once
edge_cases: []
constraints:
  - >-
    The request returns a successful response containing its run; asynchronous
    acceptance status needs shared dispatcher support.
depends_on:
  - CAP-ACC-CLS-002
  - CAP-ACC-CLS-004
related:
  - CAP-ACC-CLS-003
  - CAP-ACC-CLS-009
  - CAP-ACC-CLS-010
  - CAP-ACC-CLS-012
  - CAP-ACC-CLS-014
  - CAP-ACC-CLS-015
journeys:
  - JNY-ACC-CLS-001
  - JNY-ACC-CLS-002
  - JNY-ACC-CLS-003
  - JNY-ACC-CLS-004
open_questions:
  - OQ-ACC-CLS-001
evidence:
  - >-
    services-accounting examination inputs:
    modules/fiscal-close/aws/api/fiscal-period-close.routes.ts
    (closeFiscalPeriod), fiscal-close.schemas.ts (closeRequestSchema),
    services/jobs/close-run-actions.ts (requestPeriodClose),
    fiscal-close-jobs.ts (the workflow), fiscal-close-job-tasks.ts (lock,
    revalidate, finalize), fiscal-close-steps.ts,
    services/persistence/period-close-runs.ts (createPeriodCloseRun,
    sameRequest), db/period-close-runs.table.ts, db/fiscal-close-jobs.table.ts,
    aws/logic/fiscal-close.step-functions.ts (accounting-fiscal-close-close);
    tests integration/services/jobs/close-job.integration.spec.js,
    e2e/fiscal-close-runs.e2e.spec.js,
    db/fiscal-close.table.integration.spec.js,
    persistence/period-close-runs.integration.spec.js.
  - >-
    react-console examination inputs:
    components/accounting/close/close-dialog.tsx, close-workspace.tsx;
    acceptance/accounting/playwright/journeys/reconciliation-close/ACT-ACC-0613.spec.ts,
    ACT-ACC-0616.spec.ts, ACT-ACC-0617.spec.ts.
controls: [TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-DAT-001, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-AVL-001, TC-AVL-004, TC-MON-001, TC-MON-002, TC-MON-004, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005]
exercised_by: [FAS-ACC-CLS-03-01, FAS-ACC-CLS-03-02, FAS-ACC-CLS-03-03, FAS-ACC-CLS-03-04, FAS-ACC-CLS-03-05]
suites: [TST-ACC-CLS-001, TST-ACC-CLS-004, TST-ACC-CLS-005, TST-ACC-CLS-010, TST-ACC-CLS-017, TST-ACC-CLS-018, TST-ACC-CLS-021, TST-ACC-CLS-023, TST-ACC-CLS-029]
---

# Complete a guarded month-end close

## Purpose

A controller submits one close request whose durable execution validates the books and records the result.

## How It Works

The controller opens the close dialog and reviews a fresh evaluation. They acknowledge the displayed warning outcomes and confirm the month by typing its label. The console submits a key that survives another attempt from the same dialog. The service creates the run and job together, then starts execution after saving them. A sequence problem or competing run returns a refusal. The job takes the month and evaluates readiness again. Required blocked, pending or error results release the month and identify the failure. Warnings alone allow execution to continue. Due revenue is recognized before finalization records the close and requests aging. Future-year generation follows that successful finalization. A failure can retain the period for recovery, depending on its step and committed revenue work. The product should enforce requester identity on repeated keys, as the recorded ruling requires.

## Relationships

- Authorization establishes the permitted sequence at submission.
- Readiness supplies the execution re-check.
- Revenue, year completion and recovery explain the individual execution outcomes.
- Run tracking and the close record expose progress and retained evidence.
