Skip to content
Open
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
42 changes: 28 additions & 14 deletions desloppify/app/commands/plan/override/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,38 @@

from desloppify.app.commands.helpers.command_runtime import command_runtime
from desloppify.app.commands.helpers.state import require_issue_inventory, state_path
from desloppify.app.commands.helpers.transition_messages import emit_transition_message
from desloppify.app.commands.plan.shared.patterns import resolve_ids_from_patterns
from .io import (
_plan_file_for_state,
save_plan_state_transactional,
)
from desloppify.base.config import target_strict_score_from_config
from desloppify.base.output.terminal import colorize
from desloppify.engine.plan_state import (
load_plan,
purge_uncommitted_ids,
save_plan,
from desloppify.engine._plan.refresh_lifecycle import (
invalidate_postflight_scan,
)
from desloppify.engine._plan.sync import reconcile_plan
from desloppify.engine._plan.triage.protection import (
clear_protected_triage_artifacts,
protected_review_issue_ids,
)
from desloppify.engine._state.resolution import resolve_issues
from desloppify.engine.plan_ops import (
annotate_issue,
append_log_entry,
clear_focus,
describe_issue,
set_focus,
)
from desloppify.app.commands.helpers.transition_messages import emit_transition_message
from desloppify.engine._plan.refresh_lifecycle import (
invalidate_postflight_scan,
from desloppify.engine.plan_state import (
load_plan,
purge_uncommitted_ids,
save_plan,
)
from desloppify.engine._plan.sync import reconcile_plan
from desloppify.engine._state.resolution import resolve_issues
from desloppify.state_io import load_state

from .io import (
_plan_file_for_state,
save_plan_state_transactional,
)


def cmd_plan_describe(args: argparse.Namespace) -> None:
"""Set augmented description on issues."""
Expand Down Expand Up @@ -111,12 +116,16 @@ def cmd_plan_reopen(args: argparse.Namespace) -> None:
return

plan = load_plan(plan_file)
protected_ids = protected_review_issue_ids(plan)
clear_protected_triage_artifacts(plan, state_data)
purge_uncommitted_ids(plan, reopened)

skipped = plan.get("skipped", {})
count = 0
order = set(plan.get("queue_order", []))
for fid in reopened:
if fid in protected_ids:
continue
if fid in skipped:
skipped.pop(fid)
count += 1
Expand All @@ -127,7 +136,12 @@ def cmd_plan_reopen(args: argparse.Namespace) -> None:

append_log_entry(plan, "reopen", issue_ids=reopened, actor="user")
transition_phase: str | None = None
if invalidate_postflight_scan(plan, issue_ids=reopened, state=state_data):
executable_reopened = [fid for fid in reopened if fid not in protected_ids]
if executable_reopened and invalidate_postflight_scan(
plan,
issue_ids=executable_reopened,
state=state_data,
):
result = reconcile_plan(
plan,
state_data,
Expand Down
39 changes: 15 additions & 24 deletions desloppify/app/commands/plan/override/resolve_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from __future__ import annotations

import argparse
import logging

from desloppify.app.commands.helpers.attestation import (
show_attestation_requirement,
Expand All @@ -12,25 +11,22 @@
validate_note_length,
)
from desloppify.app.commands.helpers.command_runtime import command_runtime
from desloppify.app.commands.plan.shared.patterns import resolve_ids_from_patterns
from desloppify.app.commands.resolve.cmd import cmd_resolve
from desloppify.base.exception_sets import PLAN_LOAD_EXCEPTIONS
from desloppify.base.output.fallbacks import log_best_effort_failure
from desloppify.base.output.terminal import colorize
from desloppify.engine._plan.triage.protection import protected_review_issue_ids
from desloppify.engine._work_queue.core import ATTEST_EXAMPLE
from desloppify.engine.plan_state import (
load_plan,
save_plan,
)
from desloppify.engine.plan_ops import append_log_entry

from .resolve_helpers import (
check_cluster_guard,
split_synthetic_patterns,
)
from .resolve_workflow import resolve_workflow_patterns

logger = logging.getLogger(__name__)


def cmd_plan_resolve(args: argparse.Namespace) -> None:
"""Mark issues as fixed and delegate to resolve command UX."""
Expand Down Expand Up @@ -75,27 +71,22 @@ def cmd_plan_resolve(args: argparse.Namespace) -> None:
plan = load_plan()
if check_cluster_guard(patterns, plan, state):
return
protected_ids = sorted(
set(resolve_ids_from_patterns(state, patterns, plan=plan, status_filter="all"))
& protected_review_issue_ids(plan)
)
if protected_ids:
print(
colorize(
" Cannot resolve protected review item(s): "
+ ", ".join(protected_ids),
"red",
)
)
return
except PLAN_LOAD_EXCEPTIONS:
plan = None

try:
if plan is None:
plan = load_plan()
clusters = plan.get("clusters", {})
cluster_name = next((pattern for pattern in patterns if pattern in clusters), None)
append_log_entry(
plan,
"done",
issue_ids=patterns,
cluster_name=cluster_name,
actor="user",
note=note,
)
save_plan(plan)
except PLAN_LOAD_EXCEPTIONS as exc:
log_best_effort_failure(logger, "append plan resolve log entry", exc)
print(colorize(f" Note: unable to append plan resolve log entry ({exc}).", "dim"))

resolve_args = argparse.Namespace(
status="fixed",
patterns=patterns,
Expand Down
33 changes: 18 additions & 15 deletions desloppify/app/commands/plan/override/resolve_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,16 @@

from desloppify import state as state_mod
from desloppify.app.commands.helpers.state import state_path
from desloppify.app.commands.helpers.transition_messages import emit_transition_message
from desloppify.app.commands.plan.triage.review_coverage import (
has_open_review_issues,
)
from desloppify.app.commands.helpers.transition_messages import emit_transition_message
from desloppify.base.config import target_strict_score_from_config
from .resolve_helpers import blocked_triage_stages
from desloppify.app.commands.plan.triage.stage_queue import (
has_triage_in_queue,
inject_triage_stages,
)
from desloppify.base.config import target_strict_score_from_config
from desloppify.base.output.terminal import colorize
from desloppify.engine.plan_state import (
load_plan,
save_plan,
)
from desloppify.engine.plan_ops import (
append_log_entry,
auto_complete_steps,
purge_ids,
)
from desloppify.engine._plan.constants import (
WORKFLOW_CREATE_PLAN_ID,
WORKFLOW_SCORE_CHECKPOINT_ID,
Expand All @@ -42,13 +32,24 @@
maybe_append_entered_planning,
maybe_append_execution_drain,
)

_logger = logging.getLogger(__name__)
from desloppify.engine.plan_ops import (
append_log_entry,
auto_complete_steps,
purge_ids,
)
from desloppify.engine.plan_state import (
load_plan,
save_plan,
)
from desloppify.engine.plan_triage import (
triage_manual_stage_command,
triage_runner_commands,
)

from .resolve_helpers import blocked_triage_stages

_logger = logging.getLogger(__name__)

WORKFLOW_GATE_IDS = frozenset({WORKFLOW_SCORE_CHECKPOINT_ID, WORKFLOW_CREATE_PLAN_ID})
_WORKFLOW_PLAN_JUST_RESOLVED_KEY = "workflow_plan_just_resolved"

Expand Down Expand Up @@ -375,7 +376,9 @@ def _reconcile_if_queue_drained(
return
resolved_state_path = state_path(args)
state_data = state_mod.load_state(resolved_state_path)
if WORKFLOW_CREATE_PLAN_ID in synthetic_ids and has_open_review_issues(state_data):
if WORKFLOW_CREATE_PLAN_ID in synthetic_ids and has_open_review_issues(
state_data, plan
):
plan.setdefault("refresh_state", {})[_WORKFLOW_PLAN_JUST_RESOLVED_KEY] = True
result = reconcile_plan(
plan,
Expand Down
22 changes: 17 additions & 5 deletions desloppify/app/commands/plan/override/skip.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,17 @@
)
from desloppify.app.commands.helpers.command_runtime import command_runtime
from desloppify.app.commands.helpers.state import require_issue_inventory
from .io import (
_plan_file_for_state,
save_plan_state_transactional,
)
from desloppify.app.commands.helpers.transition_messages import emit_transition_message
from desloppify.app.commands.plan.shared.patterns import resolve_ids_from_patterns
from desloppify.base.config import target_strict_score_from_config
from desloppify.base.exception_sets import CommandError
from desloppify.base.output.terminal import colorize
from desloppify.base.output.user_message import print_user_message
from desloppify.app.commands.helpers.transition_messages import emit_transition_message
from desloppify.engine._plan.refresh_lifecycle import (
invalidate_postflight_scan,
)
from desloppify.engine._plan.sync import reconcile_plan
from desloppify.engine._plan.triage.protection import protected_review_issue_ids
from desloppify.engine.plan_ops import (
SKIP_KIND_LABELS,
append_log_entry,
Expand All @@ -44,6 +41,11 @@
save_plan,
)

from .io import (
_plan_file_for_state,
save_plan_state_transactional,
)

logger = logging.getLogger(__name__)

_BULK_SKIP_THRESHOLD = 5
Expand Down Expand Up @@ -224,6 +226,16 @@ def cmd_plan_skip(args: argparse.Namespace) -> None:
if not issue_ids:
print(colorize(" No matching issues found.", "yellow"))
return
protected_ids = sorted(set(issue_ids) & protected_review_issue_ids(plan))
if protected_ids:
print(
colorize(
" Cannot skip protected review item(s): "
+ ", ".join(protected_ids),
"red",
)
)
return

_warn_or_block_bulk_skip(issue_ids, confirm=bool(getattr(args, "confirm", False)))

Expand Down
18 changes: 10 additions & 8 deletions desloppify/app/commands/plan/triage/completion_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@
from typing import Any

from desloppify.base.output.terminal import colorize
from desloppify.engine._plan.refresh_lifecycle import current_lifecycle_phase
from desloppify.engine._state.progression import (
append_progression_event,
build_triage_complete_event,
)
from desloppify.engine._plan.constants import (
WORKFLOW_CREATE_PLAN_ID,
WORKFLOW_SCORE_CHECKPOINT_ID,
)
from desloppify.engine._plan.policy.stale import review_issue_snapshot_hash
from desloppify.engine._plan.refresh_lifecycle import mark_postflight_scan_completed
from desloppify.engine._plan.policy.stale import triage_review_issue_snapshot_hash
from desloppify.engine._plan.refresh_lifecycle import (
current_lifecycle_phase,
mark_postflight_scan_completed,
)
from desloppify.engine._state.progression import (
append_progression_event,
build_triage_complete_event,
)
from desloppify.engine.plan_ops import purge_ids
from desloppify.engine.plan_state import Cluster, PlanModel
from desloppify.engine.plan_triage import TRIAGE_IDS
Expand Down Expand Up @@ -79,7 +81,7 @@ def _sync_completion_meta(
) -> tuple[dict[str, Any], str]:
meta = ensure_triage_meta(plan)
if state.get("last_scan"):
meta["issue_snapshot_hash"] = review_issue_snapshot_hash(state)
meta["issue_snapshot_hash"] = triage_review_issue_snapshot_hash(plan, state)
elif not meta.get("issue_snapshot_hash"):
meta.pop("issue_snapshot_hash", None)

Expand Down
16 changes: 14 additions & 2 deletions desloppify/app/commands/plan/triage/confirmations/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@

from desloppify.base.output.terminal import colorize
from desloppify.base.output.user_message import print_user_message
from desloppify.engine._plan.triage.protection import (
clear_protected_triage_artifacts,
protected_review_issue_ids_from_meta,
)

from ..services import TriageServices, default_triage_services
from ..stages.records import TriageStages
from .shared import (
StageConfirmationRequest,
ensure_stage_is_confirmable,
finalize_stage_confirmation,
)
from ..services import TriageServices, default_triage_services
from ..stages.records import TriageStages

# Observe verdicts that trigger auto-skip on confirmation
_AUTO_SKIP_VERDICTS = frozenset({"false positive", "exaggerated"})
Expand Down Expand Up @@ -150,15 +154,19 @@ def _apply_observe_auto_skips(
"""
from desloppify.state_io import utc_now

clear_protected_triage_artifacts(plan)
dispositions = meta.get("issue_dispositions", {})
if not dispositions:
return 0

skipped = plan.setdefault("skipped", {})
queue_order = plan.get("queue_order", [])
protected_ids = protected_review_issue_ids_from_meta(meta)
count = 0

for issue_id, disp in dispositions.items():
if issue_id in protected_ids:
continue
verdict = disp.get("verdict", "")
if verdict not in _AUTO_SKIP_VERDICTS:
continue
Expand Down Expand Up @@ -198,8 +206,10 @@ def _undo_observe_auto_skips(plan: dict, meta: dict) -> int:

Returns the number of entries un-skipped.
"""
clear_protected_triage_artifacts(plan)
dispositions = meta.get("issue_dispositions", {})
skipped = plan.get("skipped", {})
protected_ids = protected_review_issue_ids_from_meta(meta)
count = 0

# Find all entries with decision_source == "observe_auto"
Expand All @@ -208,6 +218,8 @@ def _undo_observe_auto_skips(plan: dict, meta: dict) -> int:
if disp.get("decision_source") == "observe_auto"
}
for issue_id in auto_skipped_ids:
if issue_id in protected_ids:
continue
entry = skipped.get(issue_id)
if entry and entry.get("kind") == "triage_observe_auto":
del skipped[issue_id]
Expand Down
Loading