Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ use super::lifecycle::{
failed_backend_fallback_report,
};
use super::run_ledger::{AutomationRunLedgerRecord, AutomationTrigger};
use crate::ports::project_runtime::ProfileRuntime;
use crate::ports::project_runtime::TraceDecay;
use crate::ports::project_runtime::{AutomationProjectContext, ProfileRuntime};
use tracedecay_domain::errors::{Result, TraceDecayError};
use tracedecay_global_db::RegisteredGlobalDbLeaseV1;
use tracedecay_policy::{
Expand Down Expand Up @@ -81,14 +80,14 @@ pub struct MemoryCuratorAutomationRun {
}

pub async fn run_memory_curator_with_backend(
cg: &TraceDecay,
cg: &AutomationProjectContext,
config: &AutomationConfig,
configuration_revision_id: &ConfigurationRevisionId,
backend: &dyn AgentTaskBackend,
options: MemoryCuratorAutomationOptions,
run_control: &AutomationRunControl,
) -> AutomationRunResult<MemoryCuratorAutomationRun> {
let sessions_db = super::runner::project_automation_sessions(cg).await?;
let sessions_db = super::runner::project_automation_sessions(cg);
run_memory_curator_for_store_with_publication(
MemoryCuratorStore::Project { cg, sessions_db },
config,
Expand All @@ -107,32 +106,28 @@ pub async fn run_memory_curator_with_backend(
/// Runs one admitted retained Memory Curator effect without publishing its
/// ledger terminal before the daemon accepts the outer application terminal.
pub async fn run_memory_curator_with_backend_for_retained_settlement(
cg: &TraceDecay,
cg: &AutomationProjectContext,
config: &AutomationConfig,
configuration_revision_id: &ConfigurationRevisionId,
backend: &dyn AgentTaskBackend,
options: MemoryCuratorAutomationOptions,
run_control: &AutomationRunControl,
) -> RetainedAutomationRun<MemoryCuratorAutomationRun> {
let settlement_guard = AutomationRunSettlementGuard::new();
let result = match super::runner::project_automation_sessions(cg).await {
Ok(sessions_db) => {
run_memory_curator_for_store_with_publication(
MemoryCuratorStore::Project { cg, sessions_db },
config,
configuration_revision_id,
backend,
options,
run_control,
AutomationRunPublication {
ledger: AutomationRunLedgerPublication::DeferredUntilApplicationSettlement,
settlement_guard: Some(&settlement_guard),
},
)
.await
}
Err(error) => Err(error.into()),
};
let sessions_db = super::runner::project_automation_sessions(cg);
let result = run_memory_curator_for_store_with_publication(
MemoryCuratorStore::Project { cg, sessions_db },
config,
configuration_revision_id,
backend,
options,
run_control,
AutomationRunPublication {
ledger: AutomationRunLedgerPublication::DeferredUntilApplicationSettlement,
settlement_guard: Some(&settlement_guard),
},
)
.await;
RetainedAutomationRun::new(result, settlement_guard)
}

Expand Down Expand Up @@ -168,7 +163,7 @@ pub(crate) async fn run_user_memory_curator_with_backend(

enum MemoryCuratorStore<'a> {
Project {
cg: &'a TraceDecay,
cg: &'a AutomationProjectContext,
sessions_db: RegisteredGlobalDbLeaseV1,
},
User {
Expand All @@ -181,7 +176,7 @@ enum MemoryCuratorStore<'a> {
impl MemoryCuratorStore<'_> {
fn dashboard_root(&self) -> std::path::PathBuf {
match self {
Self::Project { cg, .. } => cg.store_layout().dashboard_root.clone(),
Self::Project { cg, .. } => cg.dashboard_root.clone(),
Self::User { profile_root, .. } => super::runner::user_automation_root(profile_root),
}
}
Expand All @@ -196,7 +191,9 @@ impl MemoryCuratorStore<'_> {

fn owner(&self) -> Result<FactOwnerV1> {
match self {
Self::Project { cg, .. } => cg.project_memory_owner(),
Self::Project { cg, .. } => Ok(FactOwnerV1::Project {
project_id: cg.project_id.clone(),
}),
Self::User { .. } => Ok(FactOwnerV1::Profile),
}
}
Expand All @@ -207,17 +204,7 @@ impl MemoryCuratorStore<'_> {
) -> Result<CurationApplyAuthorityV1> {
let actor_id = ActorId::new("automation:memory-curator").map_err(memory_contract_error)?;
let (project_id, profile_id) = match self {
Self::Project { cg, .. } => {
let project_id = match cg.project_memory_owner()? {
FactOwnerV1::Project { project_id } => project_id,
FactOwnerV1::Profile => {
return Err(memory_validation_error(
"project memory curator is missing project authority",
));
}
};
(Some(project_id), cg.profile_id().clone())
}
Self::Project { cg, .. } => (Some(cg.project_id.clone()), cg.profile_id.clone()),
Self::User { runtime, .. } => (None, runtime.profile_id().clone()),
};
Ok(CurationApplyAuthorityV1 {
Expand All @@ -230,7 +217,7 @@ impl MemoryCuratorStore<'_> {

async fn open_memory_database(&self) -> Result<tracedecay_runtime_core::db::Database> {
match self {
Self::Project { cg, .. } => cg.open_project_store_db().await,
Self::Project { cg, .. } => Ok(cg.project_memory_database.clone()),
Self::User { runtime, .. } => runtime.open_user_memory_db().await,
}
}
Expand Down
65 changes: 27 additions & 38 deletions crates/tracedecay-automation-runtime/src/automation/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ use super::skill_writer::{
activation_policy as skill_writer_activation_policy, validate_and_apply_skill_proposals,
validate_skill_proposals,
};
use crate::ports::project_runtime::ProfileRuntime;
use crate::ports::project_runtime::TraceDecay;
use crate::ports::project_runtime::{AutomationProjectContext, ProfileRuntime};
use crate::ports::session_store::AutomationSessionStore;
use tracedecay_domain::errors::{Result, TraceDecayError};
use tracedecay_global_db::{RegisteredGlobalDb, RegisteredGlobalDbLeaseV1};
Expand All @@ -52,7 +51,7 @@ use evidence::{
SkillWriterEvidenceOutcome, build_session_reflector_evidence, build_skill_writer_evidence,
canonical_evidence_hash,
};
use retrieval::{production_project_automation_retrieval, production_user_automation_retrieval};
use retrieval::{production_user_automation_retrieval, unavailable_automation_retrieval};
use session_reflector::{
ProposedAgentOutput, SessionReflectorFinalization, build_session_reflector_prompt,
finalize_session_reflector_success, validate_session_fact_candidates,
Expand Down Expand Up @@ -101,35 +100,24 @@ pub fn user_automation_root(profile_root: &std::path::Path) -> PathBuf {
profile_root.join(USER_AUTOMATION_DIR)
}

pub(super) async fn project_automation_sessions(
cg: &TraceDecay,
) -> Result<RegisteredGlobalDbLeaseV1> {
let FactOwnerV1::Project { project_id } = cg.project_memory_owner()? else {
return Err(TraceDecayError::Config {
message: "project automation requires authoritative project session scope".to_string(),
});
};
cg.project_sessions(project_id, vec![cg.store_layout().project_root.clone()])
.await
pub(super) fn project_automation_sessions(
context: &AutomationProjectContext,
) -> RegisteredGlobalDbLeaseV1 {
context.project_sessions.clone()
}

fn project_curation_authority(
cg: &TraceDecay,
context: &AutomationProjectContext,
actor: &'static str,
configuration_revision_id: &ConfigurationRevisionId,
) -> Result<CurationApplyAuthorityV1> {
let FactOwnerV1::Project { project_id } = cg.project_memory_owner()? else {
return Err(TraceDecayError::Config {
message: "project curation requires authoritative project scope".to_owned(),
});
};
let actor_id = ActorId::new(actor).map_err(|error| TraceDecayError::Config {
message: format!("invalid curation actor identity: {error}"),
})?;
Ok(CurationApplyAuthorityV1 {
actor_id,
project_id: Some(project_id),
profile_id: cg.profile_id().clone(),
project_id: Some(context.project_id.clone()),
profile_id: context.profile_id.clone(),
configuration_revision_id: configuration_revision_id.clone(),
})
}
Expand Down Expand Up @@ -423,14 +411,14 @@ impl RetainedCombinedReviewRun {
/// `prompt_version` set to the combined contract's version.
#[hotpath::measure(label = "automation.run.combined_review", future = true)]
pub async fn run_combined_review_with_backend(
cg: &TraceDecay,
cg: &AutomationProjectContext,
config: &AutomationConfig,
configuration_revision_id: &ConfigurationRevisionId,
backend: &dyn AgentTaskBackend,
options: CombinedReviewAutomationOptions,
run_control: &AutomationRunControl,
) -> Result<CombinedReviewDispatch> {
let retrieval = production_project_automation_retrieval(cg).await;
let retrieval = unavailable_automation_retrieval("session_evidence_retrieval_unavailable");
run_combined_review_for_retrieval(
cg,
config,
Expand All @@ -451,7 +439,7 @@ pub async fn run_combined_review_with_backend(
}

pub async fn run_combined_review_with_backend_and_retrieval(
cg: &TraceDecay,
cg: &AutomationProjectContext,
config: &AutomationConfig,
configuration_revision_id: &ConfigurationRevisionId,
backend: &dyn AgentTaskBackend,
Expand All @@ -476,14 +464,14 @@ pub async fn run_combined_review_with_backend_and_retrieval(
}

pub async fn run_combined_review_with_backend_for_retained_settlement(
cg: &TraceDecay,
cg: &AutomationProjectContext,
config: &AutomationConfig,
configuration_revision_id: &ConfigurationRevisionId,
backend: &dyn AgentTaskBackend,
options: CombinedReviewAutomationOptions,
run_control: &AutomationRunControl,
) -> RetainedCombinedReviewRun {
let retrieval = production_project_automation_retrieval(cg).await;
let retrieval = unavailable_automation_retrieval("session_evidence_retrieval_unavailable");
run_combined_review_with_backend_and_retrieval_for_retained_settlement(
cg,
config,
Expand All @@ -498,7 +486,7 @@ pub async fn run_combined_review_with_backend_for_retained_settlement(

#[allow(clippy::too_many_arguments)]
pub async fn run_combined_review_with_backend_and_retrieval_for_retained_settlement(
cg: &TraceDecay,
cg: &AutomationProjectContext,
config: &AutomationConfig,
configuration_revision_id: &ConfigurationRevisionId,
backend: &dyn AgentTaskBackend,
Expand Down Expand Up @@ -585,7 +573,7 @@ fn combined_skill_writer_evidence_or_not_combined(

#[hotpath::measure(future = true, label = "automation.run.combined_review.inner")]
async fn run_combined_review_for_retrieval(
cg: &TraceDecay,
cg: &AutomationProjectContext,
config: &AutomationConfig,
configuration_revision_id: &ConfigurationRevisionId,
io: AutomationTaskIo<'_>,
Expand All @@ -610,7 +598,7 @@ async fn run_combined_review_for_retrieval(
/// rather than the inlined review state machine.
#[allow(clippy::too_many_arguments)]
fn run_combined_review_for_retrieval_inner<'a>(
cg: &'a TraceDecay,
cg: &'a AutomationProjectContext,
config: &'a AutomationConfig,
configuration_revision_id: &'a ConfigurationRevisionId,
io: AutomationTaskIo<'a>,
Expand Down Expand Up @@ -640,8 +628,8 @@ fn run_combined_review_for_retrieval_inner<'a>(
reason: "combined_mode_disabled",
});
}
let dashboard_root = cg.store_layout().dashboard_root.clone();
let sessions_db = project_automation_sessions(cg).await?;
let dashboard_root = cg.dashboard_root.clone();
let sessions_db = project_automation_sessions(cg);
let _reflector_lock = match acquire_combined_task_lock(
config,
&dashboard_root,
Expand Down Expand Up @@ -670,10 +658,11 @@ fn run_combined_review_for_retrieval_inner<'a>(
Ok(lock) => lock,
Err(dispatch) => return Ok(dispatch),
};
let project_memory_db = cg.open_project_store_db().await?;
let memory = MemoryApplication::new(
cg.project_memory_owner()?,
DatabaseFactStore::new(&project_memory_db),
FactOwnerV1::Project {
project_id: cg.project_id.clone(),
},
DatabaseFactStore::new(&cg.project_memory_database),
)
.map_err(|error| TraceDecayError::Config {
message: format!("could not initialize combined review memory authority: {error}"),
Expand All @@ -689,8 +678,8 @@ fn run_combined_review_for_retrieval_inner<'a>(
let skill_bundle = match combined_skill_writer_evidence_or_not_combined(
build_skill_writer_evidence(
retrieval,
Some(cg.project_root()),
Some(cg.profile_database().as_ref()),
Some(&cg.project_root),
Some(cg.profile_database.as_ref()),
options.skill_writer,
)
.await?,
Expand Down Expand Up @@ -1039,10 +1028,10 @@ fn run_combined_review_for_retrieval_inner<'a>(

let (skill_report, skill_record, skill_committed_receipt) =
match finalize_skill_writer_success(
&cg.host_io(),
&cg.host_io,
&skill_finalizer,
&skill_bundle.profile_root,
Some(cg.store_layout().project_root.as_path()),
Some(&cg.project_root),
config,
&skill_authority,
activation_policy,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ use tracedecay_domain::{
use tracedecay_store::{StoreShardIdV1, StoreShardScopeV1};
use tracedecay_tool_catalog::{CapabilityId, UseCaseId};

use crate::ports::project_runtime::TraceDecay;
use crate::ports::session_evidence::LcmScope;
use tracedecay_contracts::request_identity::{GlobalRequestSurface, mint_global_request_id};
use tracedecay_domain::errors::{Result, TraceDecayError};
Expand Down Expand Up @@ -768,13 +767,9 @@ pub async fn registered_project_automation_retrieval(
Ok(registered_automation_retrieval_for_identity(database, identity).await)
}

pub(super) async fn production_project_automation_retrieval(
_cg: &TraceDecay,
pub(super) fn unavailable_automation_retrieval(
reason: &'static str,
) -> Box<dyn AutomationSessionRetrieval> {
unavailable_automation_retrieval("session_evidence_retrieval_unavailable")
}

fn unavailable_automation_retrieval(reason: &'static str) -> Box<dyn AutomationSessionRetrieval> {
// The static fallback session id is a fixed, valid identifier.
#[allow(clippy::expect_used)]
Box::new(UnavailableAutomationSessionRetrieval {
Expand Down
Loading
Loading