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
105 changes: 93 additions & 12 deletions apps/decodex/src/agent/tracker_tool_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ use serde::{Deserialize, Serialize};
use serde_json::{self, Value};
use time::{OffsetDateTime, format_description::well_known::Rfc3339};

#[cfg(test)]
use crate::tracker::privacy_classifier::DISABLED_PUBLIC_PROJECTION_PRIVACY_CLASSIFIER;
use crate::{
config::InternalReviewMode,
github,
prelude::eyre,
state::StateStore,
tracker::{IssueTracker, TrackerIssue, public_text},
tracker::{IssueTracker, TrackerIssue, privacy_classifier::PublicProjectionPrivacyClassifier},
workflow::WorkflowDocument,
};

Expand Down Expand Up @@ -117,6 +119,7 @@ pub(crate) struct TrackerToolBridge<'a> {
workflow: &'a WorkflowDocument,
review_context: Option<ReviewHandoffContext>,
state_store: Option<&'a StateStore>,
public_projection_privacy_classifier: &'a dyn PublicProjectionPrivacyClassifier,
pull_request_inspector: &'a dyn PullRequestInspector,
local_repo_inspector: &'a dyn LocalRepoInspector,
local_issue_state_name: RefCell<String>,
Expand All @@ -140,6 +143,7 @@ impl<'a> TrackerToolBridge<'a> {
workflow,
review_context: None,
state_store: None,
public_projection_privacy_classifier: &DISABLED_PUBLIC_PROJECTION_PRIVACY_CLASSIFIER,
pull_request_inspector: &GH_PULL_REQUEST_INSPECTOR,
local_repo_inspector: &LOCAL_GIT_REPO_INSPECTOR,
local_issue_state_name: RefCell::new(issue.state.name.clone()),
Expand All @@ -154,6 +158,7 @@ impl<'a> TrackerToolBridge<'a> {
}
}

#[cfg(test)]
fn with_review_handoff_inspectors(
tracker: &'a dyn IssueTracker,
issue: &'a TrackerIssue,
Expand All @@ -162,15 +167,38 @@ impl<'a> TrackerToolBridge<'a> {
state_store: Option<&'a StateStore>,
pull_request_inspector: &'a dyn PullRequestInspector,
local_repo_inspector: &'a dyn LocalRepoInspector,
) -> Self {
Self::with_review_handoff_options(
tracker,
issue,
workflow,
review_context,
TrackerToolBridgeOptions {
state_store,
public_projection_privacy_classifier:
&DISABLED_PUBLIC_PROJECTION_PRIVACY_CLASSIFIER,
pull_request_inspector,
local_repo_inspector,
},
)
}

fn with_review_handoff_options(
tracker: &'a dyn IssueTracker,
issue: &'a TrackerIssue,
workflow: &'a WorkflowDocument,
review_context: ReviewHandoffContext,
options: TrackerToolBridgeOptions<'a>,
) -> Self {
Self {
tracker,
issue,
workflow,
review_context: Some(review_context),
state_store,
pull_request_inspector,
local_repo_inspector,
state_store: options.state_store,
public_projection_privacy_classifier: options.public_projection_privacy_classifier,
pull_request_inspector: options.pull_request_inspector,
local_repo_inspector: options.local_repo_inspector,
local_issue_state_name: RefCell::new(issue.state.name.clone()),
local_opt_out_requested: RefCell::new(
issue.has_label(workflow.frontmatter().tracker().opt_out_label()),
Expand Down Expand Up @@ -248,21 +276,71 @@ impl<'a> TrackerToolBridge<'a> {
)
}

#[cfg(test)]
pub(crate) fn with_run_context_and_state_store(
tracker: &'a dyn IssueTracker,
issue: &'a TrackerIssue,
workflow: &'a WorkflowDocument,
review_context: ReviewHandoffContext,
state_store: &'a StateStore,
) -> Self {
Self::with_review_handoff_inspectors(
Self::with_review_handoff_options(
tracker,
issue,
workflow,
review_context,
Some(state_store),
&GH_PULL_REQUEST_INSPECTOR,
&LOCAL_GIT_REPO_INSPECTOR,
TrackerToolBridgeOptions {
state_store: Some(state_store),
public_projection_privacy_classifier:
&DISABLED_PUBLIC_PROJECTION_PRIVACY_CLASSIFIER,
pull_request_inspector: &GH_PULL_REQUEST_INSPECTOR,
local_repo_inspector: &LOCAL_GIT_REPO_INSPECTOR,
},
)
}

pub(crate) fn with_run_context_state_store_and_privacy_classifier(
tracker: &'a dyn IssueTracker,
issue: &'a TrackerIssue,
workflow: &'a WorkflowDocument,
review_context: ReviewHandoffContext,
state_store: &'a StateStore,
public_projection_privacy_classifier: &'a dyn PublicProjectionPrivacyClassifier,
) -> Self {
Self::with_review_handoff_options(
tracker,
issue,
workflow,
review_context,
TrackerToolBridgeOptions {
state_store: Some(state_store),
public_projection_privacy_classifier,
pull_request_inspector: &GH_PULL_REQUEST_INSPECTOR,
local_repo_inspector: &LOCAL_GIT_REPO_INSPECTOR,
},
)
}

#[cfg(test)]
pub(crate) fn with_review_handoff_classifier_for_test(
tracker: &'a dyn IssueTracker,
issue: &'a TrackerIssue,
workflow: &'a WorkflowDocument,
review_context: ReviewHandoffContext,
public_projection_privacy_classifier: &'a dyn PublicProjectionPrivacyClassifier,
local_repo_inspector: &'a dyn LocalRepoInspector,
) -> Self {
Self::with_review_handoff_options(
tracker,
issue,
workflow,
review_context,
TrackerToolBridgeOptions {
state_store: Some(Self::leaked_test_state_store()),
public_projection_privacy_classifier,
pull_request_inspector: &GH_PULL_REQUEST_INSPECTOR,
local_repo_inspector,
},
)
}

Expand Down Expand Up @@ -496,6 +574,13 @@ impl Display for ReviewPolicyStopRequested {

impl Error for ReviewPolicyStopRequested {}

struct TrackerToolBridgeOptions<'a> {
state_store: Option<&'a StateStore>,
public_projection_privacy_classifier: &'a dyn PublicProjectionPrivacyClassifier,
pull_request_inspector: &'a dyn PullRequestInspector,
local_repo_inspector: &'a dyn LocalRepoInspector,
}

#[derive(Clone, Debug, Eq, PartialEq)]
struct PendingReviewAction {
pr_url: String,
Expand Down Expand Up @@ -1083,10 +1168,6 @@ fn parse_github_remote_with_authority(remote_url: &str) -> std::result::Result<&
Ok(path)
}

fn validate_public_comment_body(body: &str) -> Result<(), String> {
public_text::validate_public_comment_body(body)
}

fn normalize_summary(summary: &str) -> String {
summary.split_whitespace().collect::<Vec<_>>().join(" ")
}
Expand Down
46 changes: 24 additions & 22 deletions apps/decodex/src/agent/tracker_tool_bridge/review.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,10 +426,11 @@ impl<'a> TrackerToolBridge<'a> {
"review_handoff",
&pending_review_handoff.summary,
);

tracker_tool_bridge::validate_public_comment_body(&completion_comment)
.map_err(|error| eyre::eyre!(error))?;

let projection = tracker::prepare_linear_execution_event_comment(
&completion_comment,
&handoff_record,
self.public_projection_privacy_classifier,
)?;
let success_state = self.workflow.frontmatter().tracker().success_state();
let success_state_id = self.issue.state_id_for_name(success_state).ok_or_else(|| {
eyre::eyre!(
Expand Down Expand Up @@ -461,11 +462,10 @@ impl<'a> TrackerToolBridge<'a> {
None,
);

if let Err(error) = tracker::create_linear_execution_event_comment(
if let Err(error) = tracker::create_prepared_linear_execution_event_comment(
self.tracker,
&self.issue.id,
&completion_comment,
&handoff_record,
&projection,
) {
return Err(Report::new(ReviewHandoffWritebackFailed {
issue_identifier: self.issue.identifier.clone(),
Expand All @@ -476,7 +476,7 @@ impl<'a> TrackerToolBridge<'a> {
}));
}

self.persist_linear_execution_event(&handoff_record)?;
self.persist_linear_execution_event(&projection.record)?;
self.persist_review_handoff_marker(review_context, &handoff_marker)?;
self.persist_review_orchestration_marker(review_context, &orchestration_marker)?;

Expand Down Expand Up @@ -544,10 +544,11 @@ impl<'a> TrackerToolBridge<'a> {
pull_request.head_ref_name.clone(),
pull_request.head_ref_oid.clone(),
);

tracker_tool_bridge::validate_public_comment_body(&completion_comment)
.map_err(|error| eyre::eyre!(error))?;

let projection = tracker::prepare_linear_execution_event_comment(
&completion_comment,
&handoff_record,
self.public_projection_privacy_classifier,
)?;
let state_store = self.state_store.ok_or_else(|| {
eyre::eyre!(
"Runtime state store is required to read review orchestration for issue `{}`.",
Expand All @@ -574,14 +575,13 @@ impl<'a> TrackerToolBridge<'a> {
if marker.external_round_count() >= 4 { 0 } else { marker.external_round_count() }
});

tracker::create_linear_execution_event_comment(
tracker::create_prepared_linear_execution_event_comment(
self.tracker,
&self.issue.id,
&completion_comment,
&handoff_record,
&projection,
)?;

self.persist_linear_execution_event(&handoff_record)?;
self.persist_linear_execution_event(&projection.record)?;
self.persist_review_handoff_marker(review_context, &review_handoff)?;
self.persist_review_orchestration_marker(
review_context,
Expand Down Expand Up @@ -713,17 +713,19 @@ impl<'a> TrackerToolBridge<'a> {
review_context.worktree_path,
summary,
);
let projection = tracker::prepare_linear_execution_event_comment(
&closeout_comment,
&closeout_record,
self.public_projection_privacy_classifier,
)?;

tracker_tool_bridge::validate_public_comment_body(&closeout_comment)
.map_err(|error| eyre::eyre!(error))?;
tracker::create_linear_execution_event_comment(
tracker::create_prepared_linear_execution_event_comment(
self.tracker,
&self.issue.id,
&closeout_comment,
&closeout_record,
&projection,
)?;

self.persist_linear_execution_event(&closeout_record)?;
self.persist_linear_execution_event(&projection.record)?;

Ok(())
}
Expand Down
3 changes: 3 additions & 0 deletions apps/decodex/src/agent/tracker_tool_bridge/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ use crate::{
state::{self, ReviewHandoffMarker, ReviewOrchestrationMarker, StateStore},
tracker::{
IssueTracker, TrackerComment, TrackerIssue, TrackerLabel, TrackerState, TrackerTeam,
privacy_classifier::{
PublicProjectionPrivacyClassification, PublicProjectionPrivacyClassifier,
},
records,
},
workflow::WorkflowDocument,
Expand Down
Loading