Skip to content

ADR-051: Provisioning Sources and Asymmetric Definition LifecycleΒΆ

Attribute Value
Status Proposed
Date 2026-06-12
Deciders Architecture Team
Extends ADR-050 (Definition/Instance Duality)
Related ADRs ADR-023, ADR-024, ADR-027, ADR-028, ADR-052

1. ContextΒΆ

ADR-050 introduced ResourceDefinition as the base for every catalogue entry. But definitions come from two very different origins: static reference data loaded once (session types, pod definitions, locations, policies) versus authored content continuously synced from a content package (the exam taxonomy, per-part automation). Treating them uniformly would either force a sync/version lifecycle onto inert config, or deny one to authored content that genuinely changes. The legacy services split these too β€” a DatabaseInitializer seeds YAML assets, while content is published/updated through a separate flow.

2. DecisionΒΆ

2.1 provisioning_source switchΒΆ

Every ResourceDefinition declares a provisioning_source:

Source Origin Lifecycle
seed YAML assets loaded by a seeder when the store is empty (inverse of an export/backup). None β€” static config, definition_status = published, immutable until re-seeded. No sync_status.
content_package Synced from a PAv1 content package on publish/update. draft β†’ published β†’ deprecated + a sync_status reconciled against the package.

2.2 Asymmetric lifecycleΒΆ

This is the only asymmetry in the definition model. seed definitions are inert reference data β€” no reconciliation. content_package definitions are synced, not provisioned: the content-controller owns a sync loop that maintains sync_status and advances definition_status.

stateDiagram-v2
    [*] --> Draft : created (synced from package)
    Draft --> Published : publish (content validated)
    Published --> Deprecated : superseded by newer version
    Published --> Published : re-sync (sync_status updated)
    Deprecated --> [*]
    note right of Published
        Only content_package definitions
        have this lifecycle. seed definitions
        are static config (no lifecycle).
    end note

2.3 Persistence β€” state-based, not EventStoreΒΆ

The legacy Track / Session / Pod managers are event-sourced (EventStoreDB write + Mongo read projections). LCM keeps its state-based persistence: each definition/instance is a Neuroglia AggregateRoot persisted by a MotorRepository with state_version, and the legacy event-driven behaviour is preserved as @dispatch reducers over domain events. Only the persistence substrate differs β€” no EventStore is introduced.

3. ConsequencesΒΆ

Positive β€” inert config stays inert (no spurious versioning); authored content gets a real sync/version lifecycle; one field (provisioning_source) drives the difference; reuses the existing content-sync machinery (ADR-023/024/027).

Negative / trade-offs β€” the seeder and the content-sync loop are two ingestion paths to maintain; re-seeding is a full replace (acceptable β€” local-only, no migration windows).