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
79 changes: 67 additions & 12 deletions apps/decodex/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ use crate::{
RadarValidateRequest,
},
recovery::{
self, LegacyCloseoutRecoveryRequest, ReviewHandoffAdoptRequest,
ReviewHandoffDiagnoseRequest, ReviewHandoffRebindRequest,
self, LegacyCloseoutRecoveryRequest, MergedCloseoutRecoveryRequest,
ReviewHandoffAdoptRequest, ReviewHandoffDiagnoseRequest, ReviewHandoffRebindRequest,
},
research_design::{
self, ResearchDesignCompileRequest, ResearchDesignOutcome, ResearchDesignPromoteRequest,
Expand Down Expand Up @@ -863,6 +863,15 @@ impl RecoverCommand {
manual_authority: args.manual_authority,
},
),
RecoverSubcommand::MergedCloseout(args) => recovery::run_merged_closeout(
self.project_config.as_path(),
&MergedCloseoutRecoveryRequest {
issue: args.issue.clone(),
pr_url: args.pr.clone(),
dry_run: args.dry_run,
manual_authority: args.manual_authority,
},
),
}
}
}
Expand Down Expand Up @@ -952,6 +961,22 @@ struct LegacyCloseoutRecoveryCommand {
manual_authority: bool,
}

#[derive(Debug, Args)]
struct MergedCloseoutRecoveryCommand {
/// Issue identifier for the already-merged retained lane.
#[arg(value_name = "ISSUE")]
issue: String,
/// Merged pull request URL that proves the lane's terminal code lineage.
#[arg(long, value_name = "PR_URL")]
pr: String,
/// Validate without writing closeout or cleanup ledger events.
#[arg(long)]
dry_run: bool,
/// Required for non-dry-run merged closeout reconciliation.
#[arg(long)]
manual_authority: bool,
}

#[derive(Debug, Args)]
struct ArchiveLinearCommand {
#[command(flatten)]
Expand Down Expand Up @@ -1702,6 +1727,8 @@ enum RecoverSubcommand {
ReviewHandoff(ReviewHandoffRecoveryCommand),
/// Record an audited fallback closeout for a legacy cleanup-only worktree.
LegacyCloseout(LegacyCloseoutRecoveryCommand),
/// Reconcile stale retained attention after a PR is already merged and cleaned up.
MergedCloseout(MergedCloseoutRecoveryCommand),
}

#[derive(Debug, Subcommand)]
Expand Down Expand Up @@ -1846,16 +1873,17 @@ mod tests {
CommitCommand, DiagnoseCommand, EvidenceCommand, IntakeCommand, IntakeGoalCommand,
IntakeIssuesCommand, IntakeSubcommand, LandCommand, LaneCommand, LaneInspectCommand,
LaneInterruptCommand, LaneSteerCommand, LaneSubcommand, LegacyCloseoutRecoveryCommand,
ProbeCommand, ProjectCommand, ProjectConfigArgs, ProjectSubcommand,
RadarBackfillReleaseRangeCommand, RadarBundleBuildCommand, RadarBundleCommand,
RadarBundleSubcommand, RadarBundleValidateCommand, RadarCommand, RadarLedgerCommand,
RadarLedgerIngestExistingCommand, RadarLedgerSubcommand, RadarLedgerSummaryCommand,
RadarRefreshReleaseDeltaCommand, RadarRefreshUpstreamQueueCommand,
RadarRenderSignalCommand, RadarSubcommand, RadarValidateCommand, RecoverCommand,
RecoverSubcommand, ResearchCommand, ResearchCompileCommand, ResearchOutcomeArg,
ResearchPromoteCommand, ResearchSubcommand, ReviewHandoffAdoptCommand,
ReviewHandoffDiagnoseCommand, ReviewHandoffRebindCommand, ReviewHandoffRecoveryCommand,
ReviewHandoffRecoverySubcommand, RunCommand, ServeCommand, StatusCommand,
MergedCloseoutRecoveryCommand, ProbeCommand, ProjectCommand, ProjectConfigArgs,
ProjectSubcommand, RadarBackfillReleaseRangeCommand, RadarBundleBuildCommand,
RadarBundleCommand, RadarBundleSubcommand, RadarBundleValidateCommand, RadarCommand,
RadarLedgerCommand, RadarLedgerIngestExistingCommand, RadarLedgerSubcommand,
RadarLedgerSummaryCommand, RadarRefreshReleaseDeltaCommand,
RadarRefreshUpstreamQueueCommand, RadarRenderSignalCommand, RadarSubcommand,
RadarValidateCommand, RecoverCommand, RecoverSubcommand, ResearchCommand,
ResearchCompileCommand, ResearchOutcomeArg, ResearchPromoteCommand, ResearchSubcommand,
ReviewHandoffAdoptCommand, ReviewHandoffDiagnoseCommand, ReviewHandoffRebindCommand,
ReviewHandoffRecoveryCommand, ReviewHandoffRecoverySubcommand, RunCommand, ServeCommand,
StatusCommand,
};

#[test]
Expand Down Expand Up @@ -2928,4 +2956,31 @@ mod tests {
&& pr == "https://github.com/hack-ink/pubfi-mono-v2/pull/14"
));
}

#[test]
fn parses_merged_closeout_manual_authority() {
let cli = Cli::parse_from([
"decodex",
"recover",
"merged-closeout",
"PUB-1549",
"--pr",
"https://github.com/helixbox/pubfi-mono/pull/309",
"--manual-authority",
]);

assert!(matches!(
cli.command,
Command::Recover(RecoverCommand {
command: RecoverSubcommand::MergedCloseout(MergedCloseoutRecoveryCommand {
issue,
pr,
dry_run: false,
manual_authority: true,
}),
..
}) if issue == "PUB-1549"
&& pr == "https://github.com/helixbox/pubfi-mono/pull/309"
));
}
}
2 changes: 1 addition & 1 deletion apps/decodex/src/orchestrator/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ where
add_operator_snapshot_warning(&mut snapshot, "external_observer_status_skipped");
}

refresh_operator_project_summary(&mut snapshot);
refresh_operator_project_summary(&mut snapshot, None);

Ok(snapshot)
}
Expand Down
21 changes: 15 additions & 6 deletions apps/decodex/src/orchestrator/entrypoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,10 @@ pub(crate) fn run_diagnose(request: DiagnoseRequest<'_>) -> Result<()> {
},
}?;

refresh_operator_project_summary(&mut snapshot);
refresh_operator_project_summary(
&mut snapshot,
Some(workflow.frontmatter().tracker().resolved_completed_state()),
);

let results = write_agent_evidence_snapshot(
&snapshot,
Expand Down Expand Up @@ -598,7 +601,7 @@ fn project_status_snapshot_from_operator_cache(
project_snapshot.warnings = project_warnings_from_details(&project_snapshot.warning_details);

truncate_status_snapshot_to_limit(&mut project_snapshot, limit);
refresh_operator_project_summary(&mut project_snapshot);
refresh_operator_project_summary(&mut project_snapshot, None);

Ok(project_snapshot)
}
Expand All @@ -613,7 +616,7 @@ fn mark_cached_status_snapshot(
snapshot.snapshot_age_seconds = Some(snapshot_age_seconds);

truncate_status_snapshot_to_limit(snapshot, limit);
refresh_operator_project_summary(snapshot);
refresh_operator_project_summary(snapshot, None);
}

fn truncate_status_snapshot_to_limit(snapshot: &mut OperatorStatusSnapshot, limit: usize) {
Expand Down Expand Up @@ -741,7 +744,10 @@ fn build_live_status_command_snapshot(
add_operator_snapshot_warning(&mut snapshot, &warning);
}

refresh_operator_project_summary(&mut snapshot);
refresh_operator_project_summary(
&mut snapshot,
Some(workflow.frontmatter().tracker().resolved_completed_state()),
);

if !snapshot
.connector_backoffs
Expand Down Expand Up @@ -959,7 +965,7 @@ fn build_operator_status_snapshot_for_tracker_backoff(

add_operator_snapshot_warning(&mut snapshot, "external_observer_status_skipped");
apply_terminal_history_ledger_outcomes(&mut snapshot);
refresh_operator_project_summary(&mut snapshot);
refresh_operator_project_summary(&mut snapshot, None);

Ok(snapshot)
}
Expand Down Expand Up @@ -1638,7 +1644,10 @@ fn build_operator_state_snapshot_without_live_observers(
&mut snapshot,
Some(workflow.frontmatter().tracker().resolved_completed_state()),
);
refresh_operator_project_summary(&mut snapshot);
refresh_operator_project_summary(
&mut snapshot,
Some(workflow.frontmatter().tracker().resolved_completed_state()),
);

Ok(snapshot)
}
Expand Down
Loading