|
6 | 6 | from dataclasses import dataclass |
7 | 7 |
|
8 | 8 | from . import maintenance, reconcile |
9 | | -from .context import EventContext, ExecutionResult |
| 9 | +from .context import EventContext, ExecutionResult, ManualDispatchRequest |
10 | 10 | from .event_inputs import build_event_context as decode_event_context |
11 | 11 | from .maintenance import ( |
12 | 12 | collect_status_projection_repair_items, |
@@ -249,7 +249,13 @@ def _classify_event_intent_from_context(bot: AppEventContextRuntime, context: Ev |
249 | 249 | return bot.EVENT_INTENT_NON_MUTATING_READONLY |
250 | 250 |
|
251 | 251 | if event_name == "workflow_dispatch": |
252 | | - if context.manual_action in {"show-state", "preview-check-overdue", "preview-reviewer-board"}: |
| 252 | + if context.manual_action in { |
| 253 | + "show-state", |
| 254 | + "preview-check-overdue", |
| 255 | + "preview-status-label-projection", |
| 256 | + "preview-issue314-state-health", |
| 257 | + "preview-reviewer-board", |
| 258 | + }: |
253 | 259 | return bot.EVENT_INTENT_NON_MUTATING_READONLY |
254 | 260 | return bot.EVENT_INTENT_MUTATING |
255 | 261 |
|
@@ -316,6 +322,7 @@ def execute_run(bot: AppExecutionRuntime, context: EventContext) -> ExecutionRes |
316 | 322 | workflow_run_result: reconcile.WorkflowRunHandlerResult | None = None |
317 | 323 | schedule_result: maintenance.ScheduleHandlerResult | None = None |
318 | 324 | loaded_state_hash: str | None = None |
| 325 | + manual_projection_policy: maintenance.ManualDispatchProjectionPolicy | None = None |
319 | 326 |
|
320 | 327 | try: |
321 | 328 | if ( |
@@ -352,6 +359,13 @@ def execute_run(bot: AppExecutionRuntime, context: EventContext) -> ExecutionRes |
352 | 359 | if isinstance(issue_key, str) and issue_key.isdigit() |
353 | 360 | } |
354 | 361 | loaded_epoch = state.get("freshness_runtime_epoch") if isinstance(state.get("freshness_runtime_epoch"), str) else None |
| 362 | + if event_name == "workflow_dispatch": |
| 363 | + manual_projection_policy = maintenance.derive_manual_dispatch_projection_policy( |
| 364 | + ManualDispatchRequest( |
| 365 | + action=context.manual_action or "", |
| 366 | + issue_number=context.issue_number, |
| 367 | + ) |
| 368 | + ) |
355 | 369 |
|
356 | 370 | if lock_required: |
357 | 371 | state, restored = bot.adapters.workflow.process_pass_until_expirations(state) |
@@ -442,7 +456,17 @@ def execute_run(bot: AppExecutionRuntime, context: EventContext) -> ExecutionRes |
442 | 456 | touched_items = schedule_result.touched_items |
443 | 457 | else: |
444 | 458 | touched_items = bot.drain_touched_items() |
445 | | - if lock_required and event_name in {"schedule", "workflow_dispatch"} and status_projection_repair_needed(bot, state): |
| 459 | + allow_epoch_repair_expansion = ( |
| 460 | + manual_projection_policy.allow_epoch_repair_expansion |
| 461 | + if manual_projection_policy is not None |
| 462 | + else True |
| 463 | + ) |
| 464 | + if ( |
| 465 | + lock_required |
| 466 | + and event_name in {"schedule", "workflow_dispatch"} |
| 467 | + and allow_epoch_repair_expansion |
| 468 | + and status_projection_repair_needed(bot, state) |
| 469 | + ): |
446 | 470 | touched_items = sorted( |
447 | 471 | { |
448 | 472 | *touched_items, |
@@ -542,7 +566,7 @@ def execute_run(bot: AppExecutionRuntime, context: EventContext) -> ExecutionRes |
542 | 566 | ) |
543 | 567 | _revalidate_epoch(bot, loaded_epoch, "status-label projection") |
544 | 568 | try: |
545 | | - status_labels_changed = bot.adapters.workflow.sync_status_labels_for_items(state, touched_items) |
| 569 | + status_labels_changed = bot.adapters.workflow.sync_status_labels_for_items(state, touched_items) |
546 | 570 | except RuntimeError as exc: |
547 | 571 | projection_failure = exc |
548 | 572 | _log( |
|
0 commit comments