---
id: DOM-PFM-OPS-004
title: Workspace Application
product_area: platform
module: operations
revision: 1
status: draft
realises: CON-PFM-OPS-004
design_intent: confirmed
origin: >-
  services-platform:docs/proposals/application-access-and-entitlements.md#10 Decisions (D1, D10, D11; decided
  by Andrew 2026-10-11, built and accepted 2026-10-15)
implementation_evidence: observed
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
owned_by: SVC-PFM-OPS-licensing
readable_by:
  - SVC-PFM-CSH-tenant-applications
  - SVC-PFM-CSH-console-shell
lifecycle: STATE-PFM-OPS-006
custom_fields: none
identity: >-
  The catalogue key identifies the application, and its pairing with a workspace identifies the operational
  record.
attributes:
  - name: application key
    kind: reference
    required: true
    computed: false
    notes: A stable catalogue key continues to identify a retired application.
  - name: 'name, description and display order'
    kind: text
    required: false
    computed: false
    notes: 'The catalogue stores name, optional description and order from the release’s declaration.'
  - name: licensing mode
    kind: enum
    required: true
    computed: false
    notes: The catalogue distinguishes applications requiring a licence from those always available.
  - name: participants
    kind: collection
    required: true
    computed: false
    notes: Preparation uses the declared services; no licensed application currently lists participants.
  - name: operational state
    kind: enum
    required: false
    computed: false
    notes: >-
      The four stored values are provisioning, provisioning_failed, enabled and suspended; absence is never
      provisioned.
  - name: state reason
    kind: text
    required: false
    computed: false
    notes: Suspension and failed preparation retain a reason up to one thousand characters; other states clear it.
  - name: state changed at and by
    kind: reference
    required: false
    computed: false
    notes: Change time is recorded on a row; the person is nullable for system preparation.
  - name: first enabled at
    kind: date
    required: false
    computed: false
    notes: The writer stamps the first enablement and keeps it through resumption.
  - name: availability
    kind: computed
    required: true
    computed: true
    notes: >-
      Readiness is bypassed for always-available applications; other applications require licensing and
      enabled state.
  - name: refusal reason
    kind: computed
    required: false
    computed: true
    notes: 'A commercial refusal is selected before a readiness refusal, yielding one explanation.'
relationships:
  - to: DOM-PFM-OPS-003
    cardinality: one operational record to one licence answer
    ownership: The link belongs to this object; availability is computed from both
    cross_service: false
  - to: DOM-PFM-OPS-001
    cardinality: many core application records to one provisioning job
    ownership: The related owner maintains the link; the provisioning job’s last step writes them
    cross_service: false
  - to: DOM-PFM-CSH-004
    cardinality: one availability answer to one console application entry
    ownership: The related owner maintains the link; the console presents it
    cross_service: false
invariants:
  - id: INV-DOM-PFM-OPS-004-01
    statement: The workspace-and-application primary key permits at most one operational row.
    enforced_at:
      - database
    provenance: observed
    source: >-
      services-platform:modules/licensing/db/tenant-applications.table.ts (primary key tenant_id,
      application_id)
  - id: INV-DOM-PFM-OPS-004-02
    statement: Suspension and failed preparation require a stored reason.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/e2e/licensing.e2e.spec.js#refuses a suspension with no reason,
      with 400
  - id: INV-DOM-PFM-OPS-004-03
    statement: >-
      The writer preserves first enablement; the database also rejects provisioning with a non-null
      first-enablement time.
    enforced_at:
      - service
      - database
    provenance: observed
    source: >-
      services-platform:modules/licensing/db/tenant-applications.table.ts
      (tenant_applications_first_enabled_check);
      services-platform:modules/licensing/services/persistence/tenant-applications.ts:setTenantApplicationState
  - id: INV-DOM-PFM-OPS-004-04
    statement: >-
      Availability requires an unwithheld always-available application or a licensed application whose
      operational state is enabled.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/unit/availability/application-availability.spec.ts#answers the
      available ones, and only those, from the same resolution
  - id: INV-DOM-PFM-OPS-004-05
    statement: Suspend and resume preserve application data and configuration.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/integration/services/persistence/tenant-applications.integration.spec.js#keeps
      the instant it was first enabled through a suspension and a resumption
  - id: INV-DOM-PFM-OPS-004-06
    statement: Activation and backfill insert missing core rows without updating existing operational decisions.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/integration/services/persistence/tenant-applications.integration.spec.js#leaves
      a suspended row suspended, so a redeploy never undoes an operational decision
  - id: INV-DOM-PFM-OPS-004-07
    statement: >-
      An unavailable application receives one explanation, choosing commercial refusal before readiness
      refusal.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/unit/availability/application-availability.spec.ts#gives each
      unavailable one the reason its own half of the rule refused by
  - id: INV-DOM-PFM-OPS-004-08
    statement: HQ is available by rule without an application catalogue or operational row.
    enforced_at:
      - service
    provenance: documented
    source: 'services-platform:docs/licensing.md#1. The rule'
physical:
  - applications
  - tenant_applications
evidence:
  - >-
    services-platform at ad3f6a6: modules/licensing (catalog/applications.ts, application-keys.ts,
    application-participants.ts; db/applications.table.ts, tenant-applications.table.ts;
    services/availability/application-availability.ts; services/enablement/provision-tenant-application.ts;
    services/persistence/tenant-applications.ts); modules/tenants/aws/api/tenant-applications.routes.ts;
    docs/licensing.md §1, §6.
  - 'react-console at baf6dae: components/configuration/applications; lib/applications.'
  - >-
    codex examination 2026-10-23: checked the pinned source anchors, planning references and enforcement
    boundaries; unresolved limits are recorded in module findings.
  - >-
    services-platform:modules/licensing/db/applications.table.ts:applications (description is nullable; name
    and display_order are not null).
---

# Workspace Application

## What It Models

A workspace application records the operational readiness of one application in one workspace. Licence and readiness together produce the availability answer used by the console and request authorization. A missing operational record means preparation has never been recorded.

## Identity and Attributes

The catalogue supplies the application key, name, optional description, display order and licensing mode. Participant declarations identify preparation work. Operational records hold state, any required reason, change attribution and first enablement. The service preserves first enablement; the database prohibits a non-null first-enablement time with provisioning but does not enforce immutability of that time.

## Relationships

A staff grant prepares an application only when its workspace has no operational record. Workspace activation and deployment backfill insert missing core rows. Applications needing no licence bypass readiness in availability calculation, while an active withhold still refuses them. HQ is available by rule outside this catalogue.

## Invariants

The primary key permits one row per workspace and application. Failed preparation and suspension need reasons. Staff controls check their starting state before the persistence writer locks the row, so concurrent actions are not protected from stale decisions. Outcome writes after preparation do not compare the starting state again.

## How It Is Stored and Shared

Licensing stores catalogue and operational records without workspace row policies and retains explicit scope in its statements. Security uses their availability in authorization. The workspace administrative read is declared with workspace routes and provisionally documented by Console Shell. Workspace deletion removes the operational records, and no custom fields are declared.

## Open Design Questions

An interrupted preparation can remain provisioning indefinitely. Staff retry admits failed preparation only, so a recovery mechanism still needs a decision.
