Skip to content

Registry and Service

ForgeRegistry and ForgeService bridge compiled forge artifacts into the frame lifecycle. The registry tracks lifecycle state for each artifact; the service drives state transitions in response to frame hooks.

Lifecycle States

pub enum ForgeLifecycleState {
    Pending,
    Generating,
    Compiled,
    Registered,
    Active,
}

States advance linearly in one direction only:

Pending -> Generating -> Compiled -> Registered -> Active
State Meaning
Pending Registered but generators have not run yet
Generating JTL generators are running or scheduled
Compiled compile_forge() has completed; artifact is ready
Registered Artifact registered with its frame service
Active Fully live; participates in every frame

Active is idempotent — calling advance_state() on an active record is a no-op.

ForgeRecord

pub struct ForgeRecord<TCompiled> {
    pub forge_id: String,
    pub state: ForgeLifecycleState,
    pub artifacts: TCompiled,
}

TCompiled is the domain-specific compiled artifact type (e.g. CompiledRegionArtifacts). The record owns both the lifecycle state and the artifact so they are always consistent.

// Create in Pending state
let record = ForgeRecord::new("my_region_id".to_string(), artifacts);

// Advance one step
record.advance_state()?;  // Pending -> Generating

ForgeRegistry

pub struct ForgeRegistry<TCompiled> { /* BTreeMap<String, ForgeRecord<TCompiled>> */ }

Backed by a BTreeMap for deterministic iteration order (sorted by forge_id).

Key Methods

impl<TCompiled> ForgeRegistry<TCompiled> {
    pub fn new() -> Self;

    /// Register an artifact in Pending state.
    pub fn register(&mut self, forge_id: String, artifacts: TCompiled);

    /// Look up by forge id.
    pub fn get(&self, forge_id: &str) -> Option<&ForgeRecord<TCompiled>>;
    pub fn get_mut(&mut self, forge_id: &str) -> Option<&mut ForgeRecord<TCompiled>>;

    /// Iterate all records in deterministic order.
    pub fn iter(&self) -> impl Iterator<Item = &ForgeRecord<TCompiled>>;

    /// Advance all records that are currently in `from_state`.
    pub fn advance_all_from_state(&mut self, from_state: ForgeLifecycleState);

    /// Count records in a given state.
    pub fn count_by_state(&self, state: ForgeLifecycleState) -> usize;
}

Bulk Advance Pattern

The standard usage is to call advance_all_from_state once per frame phase after the relevant work for that phase completes:

// After generators have run:
registry.advance_all_from_state(ForgeLifecycleState::Generating);

// After compile_forge calls have finished:
registry.advance_all_from_state(ForgeLifecycleState::Compiled);

ForgeService

ForgeService<TCompiled> implements plant_frame::ForgeService and drives the registry through lifecycle states in response to frame hooks.

pub struct ForgeService<TCompiled> {
    name: String,         // display name for diagnostics
    priority: u32,        // dispatch priority within frame phase (default 50)
    registry: Arc<RwLock<ForgeRegistry<TCompiled>>>,
}

The registry is wrapped in Arc<RwLock<>> so callers can hold a clone and access it across async boundaries. ForgeService is itself Clone.

Construction

let service = ForgeService::new("RegionForgeService".to_string())
    .with_priority(10);

// Access the registry from outside the service (e.g. during provision)
let registry_handle = service.registry();

Frame Hooks

ForgeService implements the ForgeService trait. During each frame phase it:

  • on_frame_init — advances all Pending records to Generating (triggers generator scheduling on the next tick).
  • on_frame_active — advances all Generating records that have completed through Compiled and Registered to Active.
  • on_frame_aggregation — (currently a no-op; reserved for future drain logic).
  • on_frame_cleanup — (currently a no-op; reserved for bookkeeping).

StateSnapshot

pub struct StateSnapshot {
    pub pending: usize,
    pub generating: usize,
    pub compiled: usize,
    pub registered: usize,
    pub active: usize,
}

Call service.state_snapshot() at any time to observe the current distribution of records across states. Useful for health checks and diagnostics endpoints.

let snap = service.state_snapshot();
println!("{} active, {} pending", snap.active, snap.pending);

Registration with FrameManager

Register your ForgeService with the FrameManager the same way as any ForgeService:

frame_manager.register_service(Box::new(region_forge_service)).await?;

See Frame Manager Reference and ForgeService Trait Reference for the full registration lifecycle.