---
id: STATE-PFM-OPS-005
title: Contract licence window standing
product_area: platform
module: operations
revision: 1
status: draft
object: DOM-PFM-OPS-003
design_intent: confirmed
origin: >-
  services-platform:docs/proposals/application-access-and-entitlements.md#D3 (seven-day grace; decided by
  Andrew 2026-10-11)
implementation_evidence: observed
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
states:
  - name: future
    meaning: The first covered day is later than the read’s calendar day.
  - name: current
    meaning: The read’s calendar day falls within the covered term.
  - name: in grace
    meaning: 'The finite term has ended, but its seven-day allowance still supplies coverage.'
  - name: expired
    meaning: 'Both term and grace have passed, so this window supplies no present coverage.'
  - name: replaced
    meaning: A successful replacement no longer retains this stored window.
transitions:
  - from: future
    to: current
    trigger: The read calendar reaches the window’s first day.
    guard: The stored window is still present.
    side_effects:
      - No persistence change occurs.
    who: 'nobody: derived from the workspace calendar'
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/unit/availability/application-availability.spec.ts#answers
      expired the day after the grace ends, which is when a licence really runs out
  - from: current
    to: in grace
    trigger: The read calendar passes the term’s last day.
    guard: The term has a finite end.
    side_effects:
      - No persistence write or access refresh occurs.
    who: 'nobody: derived'
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/unit/availability/application-availability.spec.ts#answers
      expired the day after the grace ends, which is when a licence really runs out
  - from: in grace
    to: expired
    trigger: The read calendar passes the last grace day.
    guard: This window is past grace; other windows can independently supply coverage.
    side_effects:
      - No persistence write or access refresh occurs.
    who: 'nobody: derived'
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/unit/availability/application-availability.spec.ts#answers
      expired the day after the grace ends, which is when a licence really runs out
  - from: current
    to: replaced
    trigger: A successful company read replaces the current snapshot.
    guard: The complete read and replacement succeed.
    side_effects:
      - Replace windows with the contract copy and success health atomically.
      - Request one refresh if the windows changed.
    who: the company sync
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/integration/services/sync/sync-company-licences.integration.spec.js#changes
      nothing and asks for no second flush when the read is the same as the last
  - from: in grace
    to: replaced
    trigger: A successful renewal read replaces the former grace window.
    guard: The complete read and replacement succeed.
    side_effects:
      - 'Replace the company snapshot and windows atomically, then request refresh for changed windows.'
    who: the company sync
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/unit/licences/resolve-licence-windows.spec.ts#leaves a real
      gap between two terms as two windows, because the licence did lapse
invalid:
  - from: expired
    to: current
    why: The calendar alone cannot renew a past term; a new source read must supply later coverage.
  - from: current
    to: expired
    why: 'A finite window has a grace interval, even if no read observes that interval.'
concurrency: >-
  Calendar standing is derived on each read. Snapshot replacement is atomic, but company-wide locking and
  source-version ordering are absent. Safe ordering of overlapping replacements is not established.
invariants:
  - INV-DOM-PFM-OPS-003-06
  - INV-DOM-PFM-OPS-003-08
evidence:
  - >-
    services-platform at ad3f6a6: modules/licensing/services/licences/resolve-licence-windows.ts,
    services/availability/application-availability.ts, services/sync/sync-company-licences.ts,
    db/licence-windows.table.ts; docs/licensing.md §10.
  - >-
    services-platform:modules/licensing/services/persistence/hubspot-contracts.ts:replaceCompanyContracts;
    modules/licensing/services/persistence/licence-windows.ts:replaceCompanyLicenceWindows; FND-PFM-OPS-043
  - >-
    codex examination 2026-10-23: checked the pinned source anchors, planning references and enforcement
    boundaries; unresolved limits are recorded in module findings.
---

# Contract licence window standing

## The Lifecycle

```mermaid
stateDiagram-v2
  [*] --> future: future coverage stored
  [*] --> current: current coverage stored
  future --> current: first covered day
  current --> in_grace: term complete
  in_grace --> expired: grace complete
  current --> replaced: company replacement
  in_grace --> replaced: renewed coverage
  expired --> [*]
  replaced --> [*]
```

## Transitions Explained

The read clock determines whether a stored window is future, current, in grace or past. Complete company sync replaces stored windows rather than moving a status field. A renewal adjoining the earlier term can produce one continuous replacement window.

## What Cannot Happen

A failed provider read retains stored coverage. Calendar passage cannot make an expired term current again, and no read is required to occur during every derived interval. Any stored window can disappear in a later successful replacement, including future or expired windows not drawn in the diagram.

## Concurrency and Timing

Terms and grace are evaluated at read time, so their expiry does not require nightly sync and does not request an immediate refresh. Nightly sync refreshes the commercial snapshot. Competing snapshots have an atomicity guarantee but no established ordering guarantee.
