Class: Agentilda::Unblocker

Inherits:
Object
  • Object
show all
Defined in:
lib/agentilda/unblocker.rb

Overview

Draining a plan's blocked.md, and saying what happened.

unblock is the one command a human runs because they know something the tool cannot observe: an answer has arrived. So it owes them an account of what it did with it. Four things can happen to a plan named on the command line, and only one of them is the interesting one:

* the number names no folder,
* the folder is not blocked and there is nothing to drain,
* the folder is blocked, and some, none or all of its questions fold in,
* `blocked.md` is there but numbers nothing `## B<n>`, so no program can
read it at all.

Each is reported differently, because each needs a different thing done next. This object works out which one happened; CLI::Unblock prints it.

Defined Under Namespace

Classes: Outcome, Question, Target

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tree:, agent:, executor:, root: nil, commit: false) ⇒ Unblocker

Returns a new instance of Unblocker.

Parameters:

  • tree (Agentilda::Tree)
  • agent (Agentilda::Agent)
  • executor (#call)

    receives (agent, subject, root:) and yields each phrase describing what the agent is doing

  • root (String) (defaults to: nil)

    the repository the agent works in

  • commit (Boolean) (defaults to: false)

    false plans the invocation and changes nothing



94
95
96
97
98
99
100
# File 'lib/agentilda/unblocker.rb', line 94

def initialize(tree:, agent:, executor:, root: nil, commit: false)
  @tree = tree
  @agent = agent
  @executor = executor
  @root = root || File.dirname(tree.dir)
  @commit = commit
end

Instance Attribute Details

#agentAgentilda::Agent (readonly)

Returns:



106
107
108
# File 'lib/agentilda/unblocker.rb', line 106

def agent
  @agent
end

#rootString (readonly)

Returns:

  • (String)


109
110
111
# File 'lib/agentilda/unblocker.rb', line 109

def root
  @root
end

#treeAgentilda::Tree (readonly)

Returns:



103
104
105
# File 'lib/agentilda/unblocker.rb', line 103

def tree
  @tree
end

Class Method Details

.questions(subject) ⇒ Array<Question>

The questions a folder still names, in the order the file names them.

Parameters:

Returns:



79
80
81
82
83
84
85
86
# File 'lib/agentilda/unblocker.rb', line 79

def self.questions(subject)
  answered = subject.block_answers

  subject.read("blocked.md").to_s.lines.grep(OPEN_BLOCK).map do |line|
    number = line[OPEN_BLOCK, 1].to_i
    Question.new(number:, heading: line.strip.sub(/\A\#+[ \t]*/, ""), answered: answered.include?(number))
  end
end

Instance Method Details

#call(subjects) ⇒ Array<Outcome>

Hand each folder to the agent, one at a time, and read the file back.

What the agent claims it did is not evidence. The questions are counted off disk before and after, so "folded B3" means the heading is gone from blocked.md, not that an agent said so.

Parameters:

Returns:



146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/agentilda/unblocker.rb', line 146

def call(subjects)
  return [] if subjects.empty?

  before = subjects.map { |subject| [subject.feature.ordinal.to_s, self.class.questions(subject)] }.to_h
  results = UI.concurrently(subjects, headline(subjects), jobs: 1, label: method(:label),
    fields: method(:log_fields)) do |subject, progress|
    invoke(subject, &progress)
  end

  settle
  subjects.zip(results).map { |subject, result| outcome(subject, result, before) }
end

#commit?Boolean

Returns:

  • (Boolean)


112
# File 'lib/agentilda/unblocker.rb', line 112

def commit? = @commit

#resolve(tokens) ⇒ Array<Target>

Work out what each number on the command line refers to.

Nothing is rejected here and nothing exits: a token that names no folder is as much a thing the user needs told about as a folder that drained, and stopping at the first bad one hides the rest of the report.

The gate is blocked.md itself, not the folder's emoji. ⭕️ is derived from the file, so gating on the emoji made the tool circular: a blocked.md whose questions are not numbered ## B<n> never earns the emoji, so unblock refused it, so the file could never drain and the folder could never leave 🟡.

Parameters:

  • tokens (Array<String>)

    NNN, NNN.MM, or several comma separated

Returns:



128
129
130
131
132
133
134
135
136
# File 'lib/agentilda/unblocker.rb', line 128

def resolve(tokens)
  tokens.flat_map { |token| token.to_s.split(",") }.map(&:strip).reject(&:empty?).map do |token|
    subject = tree.find(token)
    next Target.new(token:, subject: nil, problem: :missing) if subject.nil?
    next Target.new(token:, subject:, problem: :not_blocked) unless subject.file?("blocked.md")

    Target.new(token:, subject:, problem: nil)
  end
end