Tinker
Resources
Agent logs
Agent memories
Agent sessions
Agent terminal logs
Agents
Comments
Epics
Projects
Proposals
Tickets
Avo user
Resources
Agent logs
Agent memories
Agent sessions
Agent terminal logs
Agents
Comments
Epics
Projects
Proposals
Tickets
Avo user
Home
Epics
Add formal ticket dependencies: prevent orchestrator assigning blocked tasks
Edit
Add formal ticket dependencies: prevent orchestrator assigning blocked tasks
Cancel
Save
Title
*
Project
*
Choose an option
alpha
tinker
Create new project
Description
The orchestrator can assign tasks without knowing about dependencies. Blocking relationships are only stored in comments, not in the database. This causes workers to be assigned tasks they can't complete because dependencies aren't done. ## Current State **Problem:** - Dependencies only in comments (e.g., "Blocked on #131") - `is_blocked` field exists but not based on actual dependencies - Orchestrator assigns tasks without checking dependencies - Workers get stuck on tasks they can't complete **Example:** ```ruby # Ticket #130 comment: "Blocked on #131" is_blocked: false # ❌ System doesn't know! dependencies: [] # ❌ Empty! ``` Orchestrator sees #130 as available → assigns to worker → worker can't start because #131 isn't done. ## Requirements ### 1. Add Formal Dependencies Field **Database migration:** ```ruby add_column :tickets, :dependencies, :text, default: "[]" # Store as JSON array of ticket IDs ``` **Model:** ```ruby class Ticket < ApplicationRecord serialize :dependencies, JSON # Dependency relationship belongs_to :parent, class_name: 'Ticket', optional: true has_many :subtasks, class_name: 'Ticket', foreign_key: 'parent_id' # Cross-ticket dependencies (not parent-child) def dependent_tickets Ticket.where("? = ANY(dependencies)", id) end def blocking_tickets Ticket.where(id: dependencies) end def blocked? dependencies.any? { |dep_id| t = Ticket.find_by(id: dep_id) t && !t.done? } end def can_start? !blocked? end end ``` ### 2. Update Ticket Creation/Edit **When creating/editing tickets:** ```ruby # Add dependency ticket.update(dependencies: [131]) # Add multiple dependencies ticket.update(dependencies: [131, 133]) # Remove dependency ticket.update(dependencies: []) ``` ### 3. Orchestrator Filters by Dependencies **Orchestrator assigns only:** ```ruby def available_tickets Ticket.where(status: :todo) .select { |t| t.can_start? } # ✅ Check dependencies end ``` **Blocked tickets excluded:** ```ruby # #130 has dependencies: [131] # If #131 is NOT done → #130 excluded from assignable # If #131 IS done → #130 becomes assignable ``` ### 4. Update Comments to Set Dependencies **Migration script:** ```ruby # Parse comment "Blocked on #123, #456" # Extract ticket IDs # Set dependencies field Ticket.where("comments LIKE ?", "%Blocked on%").find_each do |ticket| ids = ticket.comments.scan(/Blocked on #(\d+)/).flatten ticket.update(dependencies: ids) if ids.any? end ``` ### 5. API Changes **Add to ticket API:** ```ruby # GET /api/v1/tickets/:id/dependencies # POST /api/v1/tickets/:id/dependencies (add dependency) # DELETE /api/v1/tickets/:id/dependencies/:id (remove) # Response includes: { "id": 130, "dependencies": [131], "blocking_tickets": [{id: 131, title: "...", status: "backlog"}], "blocked": true } ``` ### 6. Avo Admin - Show dependencies field - Link to dependent tickets - Visual indicator when ticket is blocked - Can't start blocked tickets (warn user) ## Acceptance Criteria - [ ] `dependencies` field added to tickets table - [ ] `blocked?` and `can_start?` methods work - [ ] Orchestrator only assigns tickets where `can_start?` is true - [ ] Existing comment-based dependencies migrated to field - [ ] API returns dependencies and blocked status - [ ] Avo shows dependencies and blocked status
Avo
· © 2026 AvoHQ ·
v3.27.0
Close modal
Are you sure?
Yes, I'm sure
No, cancel