Files
manual_slop/conductor/archive/manual_block_control_20260306/spec.md
2026-03-08 13:29:53 -04:00

4.1 KiB

Track Specification: Manual Block/Unblock Control (manual_block_control_20260306)

Overview

Allow user to manually block or unblock tickets with custom reasons. Currently blocked tickets rely solely on dependency resolution; add manual override capability.

Current State Audit

Already Implemented (DO NOT re-implement)

Ticket Status (src/models.py)

  • Ticket dataclass has status field: "todo" | "in_progress" | "completed" | "blocked"
  • blocked_reason field: Optional[str] - exists but only set by dependency cascade
  • mark_blocked(reason: str) method: Sets status="blocked", stores reason

DAG Blocking (src/dag_engine.py)

  • cascade_blocks() method: Transitively marks tickets as blocked when dependencies are blocked
  • Dependency resolution: Tickets blocked if any depends_on is not "completed"
  • No manual override exists

GUI Display (src/gui_2.py)

  • _render_ticket_dag_node(): Renders ticket nodes with status colors
  • Blocked nodes shown in distinct color
  • No block/unblock buttons

Gaps to Fill (This Track's Scope)

  • No way to manually set blocked status
  • No way to add custom block reason
  • No way to manually unblock (clear blocked status)
  • Visual indicator for manual vs dependency blocking

Architectural Constraints

DAG Validity

  • Manual block MUST trigger cascade to downstream tickets
  • Manual unblock MUST check dependencies are satisfied
  • Cannot unblock if dependencies still blocked

Audit Trail

  • Block reason MUST be stored in Ticket
  • Distinguish manual vs dependency blocking

State Synchronization

  • Block/unblock MUST update GUI immediately
  • MUST persist to track state

Architecture Reference

Key Integration Points

File Lines Purpose
src/models.py 40-60 Ticket.mark_blocked(), blocked_reason
src/dag_engine.py 30-50 cascade_blocks() - call after manual block
src/gui_2.py 2700-2800 _render_ticket_dag_node() - add buttons
src/project_manager.py 238-260 Track state persistence

Proposed Ticket Enhancement

# Add to Ticket dataclass:
manual_block: bool = False  # True if blocked manually, False if dependency

def mark_manual_block(self, reason: str) -> None:
 self.status = "blocked"
 self.blocked_reason = f"[MANUAL] {reason}"
 self.manual_block = True

def clear_manual_block(self) -> None:
 if self.manual_block:
  self.status = "todo"
  self.blocked_reason = None
  self.manual_block = False

Functional Requirements

FR1: Block Button

  • Button on each ticket node to block
  • Opens text input for block reason
  • Sets manual_block=True, calls mark_manual_block()

FR2: Unblock Button

  • Button on blocked tickets to unblock
  • Only enabled if dependencies are satisfied
  • Clears manual block, sets status to "todo"

FR3: Reason Display

  • Show block reason on hover or in node
  • Different visual for manual vs dependency block
  • Show "[MANUAL]" prefix for manual blocks

FR4: Cascade Integration

  • Manual block triggers cascade_blocks()
  • Manual unblock recalculates blocked status

Non-Functional Requirements

Requirement Constraint
Response Time Block/unblock takes effect immediately
Persistence Block state saved to track state
Visual Clarity Manual blocks clearly distinguished

Testing Requirements

Unit Tests

  • Test mark_manual_block() sets correct fields
  • Test clear_manual_block() restores todo status
  • Test cascade after manual block

Integration Tests (via live_gui fixture)

  • Block ticket via GUI, verify status changes
  • Unblock ticket, verify status restored
  • Verify cascade affects downstream tickets

Out of Scope

  • Blocking during execution (kill first, then block)
  • Scheduled/conditional blocking
  • Block templates

Acceptance Criteria

  • Block button on each ticket
  • Unblock button on blocked tickets
  • Reason input saves to ticket
  • Visual indicator distinguishes manual vs dependency
  • Reason displayed in UI
  • Cascade triggered on block/unblock
  • State persisted to track state
  • 1-space indentation maintained