Skip to main content

aivcs_core/
deploy.rs

1//! Deploy-by-digest with registry lookup.
2//!
3//! Looks up the current release for an agent, delegates to
4//! [`DeployByDigestRunner`] for deterministic run creation, and returns
5//! a [`ReplaySummary`] whose digest can be compared across identical
6//! invocations (golden equality).
7
8use chrono::{DateTime, Utc};
9use oxidized_state::storage_traits::{ReleaseRegistry, RunId, RunLedger};
10
11use crate::deploy_runner::DeployByDigestRunner;
12use crate::domain::{AivcsError, Result};
13use crate::replay::{replay_run, ReplaySummary};
14
15/// Result of a deploy-by-digest invocation.
16#[derive(Debug, Clone)]
17pub struct DeployResult {
18    /// The run ID created for this deployment.
19    pub run_id: RunId,
20    /// The spec digest from the current release.
21    pub spec_digest: String,
22    /// Replay summary with golden digest.
23    pub summary: ReplaySummary,
24}
25
26/// Deploy an agent by looking up its current release digest.
27///
28/// Resolves the agent's current release via the registry, then delegates
29/// to [`DeployByDigestRunner`] to create a deterministic run. Finally
30/// replays the run to produce a [`ReplaySummary`] suitable for golden
31/// equality testing.
32///
33/// Pass a fixed `timestamp` to get deterministic digests across invocations.
34pub async fn deploy_by_digest(
35    registry: &dyn ReleaseRegistry,
36    ledger: &dyn RunLedger,
37    agent_name: &str,
38    timestamp: Option<DateTime<Utc>>,
39) -> Result<DeployResult> {
40    // 1. Look up the current release
41    let release = registry
42        .current(agent_name)
43        .await
44        .map_err(|e| AivcsError::StorageError(e.to_string()))?
45        .ok_or_else(|| {
46            AivcsError::ReleaseConflict(format!("no current release for agent '{}'", agent_name))
47        })?;
48
49    let spec_digest_str = release.spec_digest.as_str().to_string();
50
51    // 2. Delegate to DeployByDigestRunner
52    let output = match timestamp {
53        Some(ts) => {
54            DeployByDigestRunner::run_at(ledger, &release.spec_digest, agent_name, ts).await?
55        }
56        None => DeployByDigestRunner::run(ledger, &release.spec_digest, agent_name).await?,
57    };
58
59    // 3. Replay to get the golden digest
60    let (_events, replay_summary) = replay_run(ledger, &output.run_id.0).await?;
61
62    Ok(DeployResult {
63        run_id: output.run_id,
64        spec_digest: spec_digest_str,
65        summary: replay_summary,
66    })
67}