maf 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +43 -0
- data/README.md +18 -1
- data/assets/agents-contract.md +2 -0
- data/assets/coord +145 -21
- data/assets/dispatcher +59 -13
- data/install.md +6 -1
- data/lib/maf/bootstrap/claude_settings.rb +17 -9
- data/lib/maf/bootstrap.rb +4 -0
- data/lib/maf/cli.rb +19 -9
- data/lib/maf/flow/effort_report.rb +20 -0
- data/lib/maf/flow/generator.rb +2 -1
- data/lib/maf/flow/hermes_env_setup.rb +46 -0
- data/lib/maf/flow/manifest.rb +1 -1
- data/lib/maf/flow/models.rb +11 -0
- data/lib/maf/flow/options.rb +29 -3
- data/lib/maf/flow/prompt_text.rb +6 -3
- data/lib/maf/flow/report.rb +6 -5
- data/lib/maf/flow/role_catalog.rb +1 -1
- data/lib/maf/flow/role_files.rb +5 -5
- data/lib/maf/flow/roster.rb +6 -3
- data/lib/maf/flow.rb +3 -1
- data/lib/maf/menu.rb +48 -14
- data/lib/maf/setup_agent/args.rb +16 -10
- data/lib/maf/setup_agent/dispatch.rb +2 -2
- data/lib/maf/setup_agent/launcher.rb +6 -4
- data/lib/maf/setup_agent/manifest.rb +9 -3
- data/lib/maf/setup_agent.rb +11 -11
- data/lib/maf/shared/harness/defaults.rb +10 -3
- data/lib/maf/shared/harnesses/claude.rb +6 -3
- data/lib/maf/shared/harnesses/codex.rb +39 -10
- data/lib/maf/shared/harnesses/cursor.rb +60 -4
- data/lib/maf/shared/harnesses/hermes.rb +21 -11
- data/lib/maf/shared/harnesses/opencode.rb +25 -2
- data/lib/maf/shared/roles.rb +4 -0
- data/lib/maf/team.rb +5 -4
- data/lib/maf/team_command.rb +3 -1
- data/lib/maf/untrack.rb +7 -0
- data/lib/maf/version.rb +1 -1
- data/lib/maf/worker_control.rb +12 -1
- data/lib/maf/worker_list.rb +83 -0
- data/templates/roles.yml +14 -0
- metadata +4 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# worker_list.rb - list the workers that run now: sessions and dispatchers.
|
|
4
|
+
require "json"
|
|
5
|
+
require "time"
|
|
6
|
+
require_relative "workers"
|
|
7
|
+
require_relative "shared/processes"
|
|
8
|
+
|
|
9
|
+
module Maf
|
|
10
|
+
# WorkerList shows each running worker process of the project. A worker
|
|
11
|
+
# can run twice, for example as a session and as a dispatcher.
|
|
12
|
+
class WorkerList
|
|
13
|
+
Row = Struct.new(:worker, :role, :harness, :mode, :pid, :started)
|
|
14
|
+
HEADER = %w[WORKER ROLE HARNESS MODE PID STARTED].freeze
|
|
15
|
+
LSTART = "%a %b %d %H:%M:%S %Y %z"
|
|
16
|
+
|
|
17
|
+
def initialize(root)
|
|
18
|
+
@root = root
|
|
19
|
+
@workers = Workers.at(root)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run
|
|
23
|
+
rows = running
|
|
24
|
+
puts rows.empty? ? "No worker runs." : table([HEADER] + rows.map { |row| row.to_a.map(&:to_s) }), warnings(rows)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def running = (sessions + dispatchers + registered).uniq(&:pid).sort_by { |row| [row.worker, row.mode] }
|
|
30
|
+
|
|
31
|
+
def coord_dir = File.join(@root, ".maf", "coordination")
|
|
32
|
+
def records(dir, pattern) = Dir.glob(File.join(coord_dir, dir, pattern)).map { |path| [path, read(path)] }
|
|
33
|
+
|
|
34
|
+
def sessions
|
|
35
|
+
records("sessions", "*.maf.json").filter_map do |path, record|
|
|
36
|
+
row(record, "session") if session_live?(record["pid"], File.mtime(path))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def session_live?(pid, registered)
|
|
41
|
+
started = Shared::Processes.started_at(pid)
|
|
42
|
+
Shared::Processes.alive?(pid) && !started.empty? && Time.strptime("#{started} +0000", LSTART) <= registered
|
|
43
|
+
rescue ArgumentError
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def dispatchers
|
|
48
|
+
records("presence", "*.json").filter_map do |_, entry|
|
|
49
|
+
row(entry, "dispatch") if entry["mode"] == "dispatch" && Shared::Processes.live?(entry)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def registered
|
|
54
|
+
@workers.all.filter_map do |worker, entry|
|
|
55
|
+
row(entry.merge("worker" => worker), "dispatch") if @workers.dispatcher?(worker)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def row(data, mode)
|
|
60
|
+
entry = @workers.find(data["worker"]).to_h
|
|
61
|
+
Row.new(data["worker"], data["role"] || entry["role"], data["harness"] || entry["harness"], mode,
|
|
62
|
+
data["pid"].to_i, Shared::Processes.started_at(data["pid"]))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def table(lines)
|
|
66
|
+
widths = lines.transpose.map { |column| column.map(&:size).max }
|
|
67
|
+
lines.map { |line| line.zip(widths).map { |text, width| text.ljust(width) }.join(" ").rstrip }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def warnings(rows)
|
|
71
|
+
twice = rows.group_by(&:worker).select { |_, list| list.size > 1 }
|
|
72
|
+
stopped = @workers.all.keys - rows.map(&:worker)
|
|
73
|
+
twice.map { |worker, list| "warning: #{worker} runs #{list.size} times (pids #{list.map(&:pid).join(", ")})" } +
|
|
74
|
+
(stopped.empty? ? [] : ["stopped: #{stopped.join(", ")}"])
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def read(path)
|
|
78
|
+
JSON.parse(File.read(path))
|
|
79
|
+
rescue JSON::ParserError, SystemCallError
|
|
80
|
+
{}
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
data/templates/roles.yml
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
# the model with --model ROLE=MODEL. If the user does not choose a model,
|
|
5
5
|
# claude roles get lib/maf/flow.rb's DEFAULT_MODELS entry (claude-opus-5-5).
|
|
6
6
|
#
|
|
7
|
+
# `effort` is the default effort level. A role gets it when the harness lists
|
|
8
|
+
# the level. --effort ROLE=LEVEL overrides it. A lower level uses fewer tokens.
|
|
9
|
+
#
|
|
7
10
|
# `description` is also the routing line in the architect's "Available roles"
|
|
8
11
|
# list. Keep it short and specific to the role.
|
|
9
12
|
#
|
|
@@ -42,6 +45,7 @@ roles:
|
|
|
42
45
|
- If a message starts with "ESCALATION", a role cannot fix a problem. Tell the user in plain words:
|
|
43
46
|
what failed, which task, and the impact. Give two or three options with a recommended one.
|
|
44
47
|
Wait for the answer. Then send the answer to the role with `coord msg`.
|
|
48
|
+
If the escalation names a blocked task, run `coord unblock <id>` after you send the answer.
|
|
45
49
|
- If the architect escalates a repeated review failure, forward the
|
|
46
50
|
options to the user. Do not send a new goal until the user decides.
|
|
47
51
|
|
|
@@ -53,6 +57,7 @@ roles:
|
|
|
53
57
|
- Process detail in reports. The user needs outcomes and decisions.
|
|
54
58
|
- Optimistic status. Report blockers and failed checks as they are.
|
|
55
59
|
model_hint: "Strongest reasoning and long context, conversational. Cloud: Claude Opus 5.5 (default for the claude harness), GPT-5.x."
|
|
60
|
+
effort: medium
|
|
56
61
|
can_edit: false
|
|
57
62
|
|
|
58
63
|
architect:
|
|
@@ -84,6 +89,8 @@ roles:
|
|
|
84
89
|
Goal, Inputs, Out of scope, Acceptance, Report format.
|
|
85
90
|
A worker drifts if a field is missing.
|
|
86
91
|
- Write acceptance criteria as exact test files or commands. The worker runs each one before coord done.
|
|
92
|
+
- A message `BLOCKED task <id> (dependency)` waits for other work. Plan or order that work.
|
|
93
|
+
When it lands, run `coord unblock <id>`. A blocked task stays out of `coord next` until then.
|
|
87
94
|
- If a review rejects the same kind of problem two times, stop the fix
|
|
88
95
|
loop. Send the project manager two or three options with the cost of
|
|
89
96
|
each option. A third patch of the same kind usually fails too.
|
|
@@ -96,6 +103,7 @@ roles:
|
|
|
96
103
|
- Code in task specs. Describe behavior and constraints instead.
|
|
97
104
|
- Trust in a worker report without a diff check.
|
|
98
105
|
model_hint: "Strongest reasoning and long context. Cloud: Claude Opus 5.5 (default for the claude harness), GPT-5.x, deepseek-v4."
|
|
106
|
+
effort: high
|
|
99
107
|
can_edit: false
|
|
100
108
|
|
|
101
109
|
backend-developer:
|
|
@@ -123,6 +131,7 @@ roles:
|
|
|
123
131
|
- Edits to a test that looks wrong. Report the test to the architect.
|
|
124
132
|
- Claims about code that you did not read.
|
|
125
133
|
model_hint: "A strong code model. A local Qwen3.8-27B is enough for well-scoped tasks; use a cloud coder for hard or large changes."
|
|
134
|
+
effort: medium
|
|
126
135
|
can_edit: true
|
|
127
136
|
|
|
128
137
|
frontend-developer:
|
|
@@ -149,6 +158,7 @@ roles:
|
|
|
149
158
|
- Values that are hard-coded to make a test pass.
|
|
150
159
|
- Visual changes outside the task scope.
|
|
151
160
|
model_hint: "Same as backend-developer: local coder for scoped work, cloud coder for hard UI work."
|
|
161
|
+
effort: medium
|
|
152
162
|
can_edit: true
|
|
153
163
|
|
|
154
164
|
tester:
|
|
@@ -174,6 +184,7 @@ roles:
|
|
|
174
184
|
- Assertions that only repeat the implementation.
|
|
175
185
|
- Deleted or weakened tests. A test removal needs architect approval.
|
|
176
186
|
model_hint: "Mechanical test work usually fits a local coder model; use cloud for subtle failure analysis."
|
|
187
|
+
effort: low
|
|
177
188
|
can_edit: true
|
|
178
189
|
|
|
179
190
|
reviewer:
|
|
@@ -216,6 +227,7 @@ roles:
|
|
|
216
227
|
- Style comments that the project's linter already covers.
|
|
217
228
|
- A warning or minor finding labeled critical to force another round.
|
|
218
229
|
model_hint: "Judgment-heavy. Prefer a cloud model with strong review ability."
|
|
230
|
+
effort: high
|
|
219
231
|
can_edit: false
|
|
220
232
|
|
|
221
233
|
skeptic:
|
|
@@ -252,6 +264,7 @@ roles:
|
|
|
252
264
|
- A warning or minor finding labeled critical to force another round.
|
|
253
265
|
- Style comments. The reviewer and the linter cover style.
|
|
254
266
|
model_hint: "Judgment-heavy. Use a different model family from reviewer and auditor, for example GPT-5.x if reviewer runs Claude. Different models miss different defects."
|
|
267
|
+
effort: high
|
|
255
268
|
can_edit: false
|
|
256
269
|
|
|
257
270
|
auditor:
|
|
@@ -286,4 +299,5 @@ roles:
|
|
|
286
299
|
- A warning or minor finding labeled critical to force another round.
|
|
287
300
|
- Style comments. The reviewer and the linter cover style.
|
|
288
301
|
model_hint: "Judgment-heavy. Use a different model family from reviewer and skeptic, for example Gemini, Grok, or a strong local model. Different models miss different defects."
|
|
302
|
+
effort: high
|
|
289
303
|
can_edit: false
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: maf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dominik Alberski
|
|
@@ -83,8 +83,10 @@ files:
|
|
|
83
83
|
- lib/maf/flow/agent_links.rb
|
|
84
84
|
- lib/maf/flow/bootstrapper.rb
|
|
85
85
|
- lib/maf/flow/codex_hooks.rb
|
|
86
|
+
- lib/maf/flow/effort_report.rb
|
|
86
87
|
- lib/maf/flow/generator.rb
|
|
87
88
|
- lib/maf/flow/harness_linker.rb
|
|
89
|
+
- lib/maf/flow/hermes_env_setup.rb
|
|
88
90
|
- lib/maf/flow/hermes_hook.rb
|
|
89
91
|
- lib/maf/flow/hermes_hook_setup.rb
|
|
90
92
|
- lib/maf/flow/hook_files.rb
|
|
@@ -178,6 +180,7 @@ files:
|
|
|
178
180
|
- lib/maf/vm/template.rb
|
|
179
181
|
- lib/maf/worker_archive.rb
|
|
180
182
|
- lib/maf/worker_control.rb
|
|
183
|
+
- lib/maf/worker_list.rb
|
|
181
184
|
- lib/maf/workers.rb
|
|
182
185
|
- templates/role-stub.yml.erb
|
|
183
186
|
- templates/roles.yml
|