Skip to content
Draft
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
26 changes: 16 additions & 10 deletions desloppify/app/commands/plan/triage/confirmations/enrich.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,26 @@
from desloppify.base.output.terminal import colorize
from desloppify.base.output.user_message import print_user_message

from .basic import MIN_ATTESTATION_LEN, validate_attestation
from .shared import (
StageConfirmationRequest,
ensure_stage_is_confirmable,
finalize_stage_confirmation,
)
from ..services import TriageServices, default_triage_services
from ..stages.helpers import scoped_manual_clusters_with_issues
from ..review_coverage import active_triage_issue_ids
from ..stages.helpers import (
active_triage_issue_scope,
scoped_manual_clusters_with_issues,
)
from ..validation.enrich_quality import (
EnrichQualityIssue as _ConfirmationCheckIssue,
)
from ..validation.enrich_quality import (
EnrichQualityReport as _ConfirmationCheckReport,
)
from ..validation.enrich_quality import (
evaluate_enrich_quality,
)
from .basic import MIN_ATTESTATION_LEN, validate_attestation
from .shared import (
StageConfirmationRequest,
ensure_stage_is_confirmable,
finalize_stage_confirmation,
)


def _print_confirmation_failure(
Expand Down Expand Up @@ -179,7 +185,7 @@ def confirm_enrich(
checks = _collect_enrich_level_confirmation_checks(
plan,
include_stale_issue_ref_warning=True,
triage_issue_ids=active_triage_issue_ids(plan, state) or None,
triage_issue_ids=active_triage_issue_scope(plan, state),
)

print(colorize(" Stage: ENRICH — Make steps executor-ready (detail, refs)", "bold"))
Expand Down Expand Up @@ -232,7 +238,7 @@ def confirm_sense_check(
checks = _collect_enrich_level_confirmation_checks(
plan,
include_stale_issue_ref_warning=False,
triage_issue_ids=active_triage_issue_ids(plan, state) or None,
triage_issue_ids=active_triage_issue_scope(plan, state),
)

print(colorize(" Stage: SENSE-CHECK — Verify accuracy & cross-cluster deps", "bold"))
Expand Down
30 changes: 13 additions & 17 deletions desloppify/app/commands/plan/triage/runner/stage_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,31 @@

from desloppify.engine.plan_triage import TriageInput

from ..completion_flow import count_log_activity_since
from ..observe_batches import observe_dimension_breakdown
from ..review_coverage import cluster_issue_ids, open_review_ids_from_state
from ..stages.evidence_parsing import (
parse_observe_evidence,
parse_value_check_decision_ledger,
validate_observe_evidence,
validate_reflect_skip_evidence,
validate_report_has_file_paths,
validate_report_references_clusters,
)
from ..validation.enrich_quality import evaluate_enrich_quality
from ..validation.completion_policy import evaluate_completion_readiness
from ..validation.enrich_checks import (
_cluster_file_overlaps,
_clusters_with_directory_scatter,
_clusters_with_high_step_ratio,
)
from ..completion_flow import count_log_activity_since
from ..observe_batches import observe_dimension_breakdown
from ..review_coverage import (
active_triage_issue_ids,
cluster_issue_ids,
open_review_ids_from_state,
)
from ..stages.helpers import (
active_triage_issue_scope,
scoped_manual_clusters_with_issues,
unclustered_review_issues,
unenriched_clusters,
value_check_targets,
)
from ..stages.evidence_parsing import parse_value_check_decision_ledger
from ..validation.completion_policy import evaluate_completion_readiness
from ..validation.enrich_checks import (
_cluster_file_overlaps,
_clusters_with_directory_scatter,
_clusters_with_high_step_ratio,
)
from ..validation.enrich_quality import evaluate_enrich_quality


@dataclass(frozen=True)
Expand Down Expand Up @@ -238,7 +234,7 @@ def _validate_enrich_stage(
plan,
repo_root,
phase_label="enrich",
triage_issue_ids=active_triage_issue_ids(plan, state) or None,
triage_issue_ids=active_triage_issue_scope(plan, state),
)
if failures:
return False, failures[0].message
Expand All @@ -260,7 +256,7 @@ def _validate_sense_check_stage(
if len(report) < 100:
return False, f"Sense-check report too short ({len(report)} chars, need 100+)."
manual_clusters = scoped_manual_clusters_with_issues(plan, state)
triage_issue_ids = active_triage_issue_ids(plan, state) or None
triage_issue_ids = active_triage_issue_scope(plan, state)
triage_scope = active_triage_issue_scope(plan, state)
open_review_ids = open_review_ids_from_state(state) if triage_scope is None else triage_scope
if not open_review_ids and not manual_clusters:
Expand Down
26 changes: 15 additions & 11 deletions desloppify/app/commands/plan/triage/stages/enrich.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@
from desloppify.base.output.user_message import print_user_message
from desloppify.engine.plan_triage import compute_triage_progress

from .records import record_enrich_stage, resolve_reusable_report
from ..validation.enrich_quality import evaluate_enrich_quality
from ..completion_flow import count_log_activity_since
from ..review_coverage import (
open_review_ids_from_state,
)
from ..services import TriageServices, default_triage_services
from ..stage_queue import has_triage_in_queue, print_cascade_clear_feedback
from ..validation.enrich_checks import (
_enrich_report_or_error,
_require_organize_stage_for_enrich,
_steps_with_bad_paths,
_steps_without_effort,
_underspecified_steps,
)
from ..completion_flow import count_log_activity_since
from ..review_coverage import (
active_triage_issue_ids,
open_review_ids_from_state,
)
from ..stage_queue import has_triage_in_queue, print_cascade_clear_feedback
from ..services import TriageServices, default_triage_services
from ..validation.enrich_quality import evaluate_enrich_quality
from .helpers import active_triage_issue_scope
from .records import record_enrich_stage, resolve_reusable_report

ColorizeFn = Callable[[str, str], str]

Expand Down Expand Up @@ -120,7 +120,11 @@ def _require_cluster_update_activity(
return True
activity = deps.count_log_activity_since(plan, organize_ts)
update_ops = activity.get("cluster_update", 0)
if update_ops != 0 or not open_review_ids_from_state(state):
triage_scope = active_triage_issue_scope(plan, state)
open_review_ids = (
open_review_ids_from_state(state) if triage_scope is None else triage_scope
)
if update_ops != 0 or not open_review_ids:
return True
if attestation and len(attestation.strip()) >= 40:
print(
Expand Down Expand Up @@ -268,7 +272,7 @@ def run_stage_enrich(
if get_project_root is None:
from desloppify.base.discovery.paths import get_project_root

triage_ids = active_triage_issue_ids(plan, state) or None
triage_ids = active_triage_issue_scope(plan, state)
quality_report = evaluate_enrich_quality(
plan,
get_project_root(),
Expand Down
30 changes: 20 additions & 10 deletions desloppify/app/commands/plan/triage/stages/organize.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,11 @@

from desloppify.base.output.terminal import colorize

from ..display.dashboard import print_organize_result
from ..completion_flow import count_log_activity_since
from ..display.dashboard import print_organize_result
from ..review_coverage import open_review_ids_from_state
from ..stage_queue import has_triage_in_queue
from ..services import TriageServices, default_triage_services
from ..validation.stage_policy import (
ReflectAutoConfirmDeps,
auto_confirm_reflect_for_organize,
)
from ..stage_queue import has_triage_in_queue
from ..validation.organize_policy import (
_clusters_enriched_or_error,
_manual_clusters_or_error,
Expand All @@ -23,7 +19,12 @@
_validate_organize_against_ledger_or_error,
validate_backlog_promotions_executed,
)
from ..validation.stage_policy import require_prerequisite
from ..validation.stage_policy import (
ReflectAutoConfirmDeps,
auto_confirm_reflect_for_organize,
require_prerequisite,
)
from .helpers import active_triage_issue_scope, triage_scoped_plan
from .records import record_organize_stage


Expand Down Expand Up @@ -110,7 +111,10 @@ def _validate_organize_submission(
is_reuse: bool,
services: TriageServices,
) -> tuple[list[str], str] | None:
open_review_ids = open_review_ids_from_state(state)
triage_scope = active_triage_issue_scope(plan, state)
open_review_ids = (
open_review_ids_from_state(state) if triage_scope is None else triage_scope
)
triage_input = services.collect_triage_input(plan, state)
if not auto_confirm_reflect_for_organize(
args=args,
Expand All @@ -125,7 +129,10 @@ def _validate_organize_submission(
):
return None

manual_clusters = _manual_clusters_or_error(plan, open_review_ids=open_review_ids)
manual_clusters = _manual_clusters_or_error(
triage_scoped_plan(plan, state),
open_review_ids=open_review_ids,
)
if manual_clusters is None:
return None
if not _clusters_enriched_or_error(plan, state):
Expand Down Expand Up @@ -233,7 +240,10 @@ def _cmd_stage_organize(

runtime = resolved_services.command_runtime(args)
state = runtime.state
open_review_ids = open_review_ids_from_state(state)
triage_scope = active_triage_issue_scope(plan, state)
open_review_ids = (
open_review_ids_from_state(state) if triage_scope is None else triage_scope
)

validated = _validate_organize_submission(
args=args,
Expand Down
28 changes: 17 additions & 11 deletions desloppify/app/commands/plan/triage/stages/sense_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,24 @@

from desloppify.base.output.terminal import colorize

from .records import record_sense_check_stage, resolve_reusable_report
from .helpers import value_check_targets
from ..validation.enrich_quality import evaluate_enrich_quality
from ..review_coverage import open_review_ids_from_state
from ..services import TriageServices, default_triage_services
from ..stage_queue import has_triage_in_queue, print_cascade_clear_feedback
from ..validation.enrich_checks import (
_steps_missing_issue_refs,
_steps_with_bad_paths,
_steps_with_vague_detail,
_steps_without_effort,
_underspecified_steps,
)
from ..review_coverage import active_triage_issue_ids, open_review_ids_from_state
from ..stage_queue import has_triage_in_queue, print_cascade_clear_feedback
from ..services import TriageServices, default_triage_services
from ..validation.enrich_quality import evaluate_enrich_quality
from .enrich import ColorizeFn
from .helpers import (
active_triage_issue_scope,
scoped_manual_clusters_with_issues,
value_check_targets,
)
from .records import record_sense_check_stage, resolve_reusable_report


@dataclass(frozen=True)
Expand Down Expand Up @@ -66,7 +70,7 @@ def _sense_check_quality_problems(
from desloppify.base.discovery.paths import get_project_root

repo_root = get_project_root()
triage_ids = active_triage_issue_ids(plan, state) or None
triage_ids = active_triage_issue_scope(plan, state)
quality_report = evaluate_enrich_quality(
plan,
repo_root,
Expand Down Expand Up @@ -118,13 +122,15 @@ def _sense_check_evidence_failures(
validate_report_has_file_paths,
validate_report_references_clusters,
)
from ..review_coverage import manual_clusters_with_issues

failures: list[object] = []
if open_review_ids_from_state(state):
triage_scope = active_triage_issue_scope(plan, state)
open_review_ids = (
open_review_ids_from_state(state) if triage_scope is None else triage_scope
)
if open_review_ids:
failures.extend(validate_report_has_file_paths(report) or [])

cluster_names = manual_clusters_with_issues(plan)
cluster_names = scoped_manual_clusters_with_issues(plan, state)
if cluster_names:
failures.extend(validate_report_references_clusters(report, cluster_names) or [])

Expand Down
12 changes: 11 additions & 1 deletion desloppify/app/commands/plan/triage/stages/strategize.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,14 @@ def _create_strategic_work_items(
plan: dict,
strategic_issues: list[dict],
) -> None:
"""Create work items in state and insert IDs at front of queue_order."""
"""Create work items in state and insert IDs at front of queue_order.

A strategist can reuse an identifier from an older cycle. Existing skips,
especially protected false-positive and permanent skips, are deliberate
plan intent and must not be silently revived by a generated report. The
new assessment remains available in the strategist briefing, while the
skipped ID stays out of the execution queue.
"""
work_items = state.setdefault("work_items", {})
if not work_items:
issues = state.get("issues")
Expand All @@ -150,10 +157,13 @@ def _create_strategic_work_items(
state["work_items"] = work_items

queue_order = plan.setdefault("queue_order", [])
skipped = plan.setdefault("skipped", {})
new_ids: list[str] = []

for entry in strategic_issues:
issue_id = f"strategy::{entry['identifier']}"
if issue_id in skipped:
continue
work_items[issue_id] = {
"status": "open",
"detector": "strategy",
Expand Down
11 changes: 6 additions & 5 deletions desloppify/app/commands/plan/triage/validation/enrich_quality.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from pathlib import Path
from typing import Literal

from ..review_coverage import cluster_issue_ids
from .enrich_checks import (
_steps_missing_issue_refs,
_steps_referencing_skipped_issues,
Expand All @@ -14,7 +15,6 @@
_steps_without_effort,
_underspecified_steps,
)
from ..review_coverage import cluster_issue_ids

Severity = Literal["failure", "warning"]

Expand Down Expand Up @@ -77,17 +77,18 @@ def _active_cluster_names(plan: dict, triage_issue_ids: set[str]) -> set[str]:
return {
name
for name, cluster in plan.get("clusters", {}).items()
if not cluster_issue_ids(cluster)
or set(cluster_issue_ids(cluster)) & triage_issue_ids
if set(cluster_issue_ids(cluster)) & triage_issue_ids
}


def _scoped_plan(plan: dict, triage_issue_ids: set[str] | None) -> dict:
"""Narrow plan to only clusters relevant to the current triage cycle.

Returns the original plan unchanged when no triage scoping is needed.
``None`` means no triage scoping is needed. An empty set means a frozen
triage cycle has no remaining live issues, so no historical clusters may
leak back into validation.
"""
if not triage_issue_ids:
if triage_issue_ids is None:
return plan
active = _active_cluster_names(plan, triage_issue_ids)
return {
Expand Down
Loading