1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
-- subagents/profiles.lua: two-layer recursive discovery over real directories.
--
-- Discovery is exercised against temporary directories rather than the machine's
-- real config, by passing explicit roots to `discover` — the same seam the
-- extension uses for the user and project layers, in the same order.
--
-- Needs luv (the recursive walk) and lyaml (the frontmatter); without either the
-- whole file skips rather than asserting on a degraded parse.
local profiles = require("subagents.profiles")
local function write(path, text)
assert(os.execute("mkdir -p " .. (path:match("^(.*)/[^/]+$") or ".")))
local file = assert(io.open(path, "w"))
file:write(text)
file:close()
end
-- Build both layers once; every case reads the same discovery result.
local function discover_fixture()
local ok_uv, uv = pcall(require, "luv")
if not ok_uv then
return nil, "luv is not installed"
end
if not pcall(require, "lyaml") then
return nil, "lyaml is not installed"
end
local tmp = assert(uv.fs_mkdtemp("/tmp/panto-subagents-profiles-XXXXXX"))
local user = tmp .. "/user/agents"
local project = tmp .. "/project/agents"
write(user .. "/reviewer.md", table.concat({
"---",
"description: Reviews changes",
"model: anthropic:sonnet",
"reasoning: high",
"---",
"You are a reviewer.",
"",
}, "\n"))
write(user .. "/nested/deeper/planner.md", "You plan.\n")
write(user .. "/shared.md", "---\nname: shared\ndescription: from the user layer\n---\nuser body\n")
write(user .. "/renamed.md", "---\nname: from-frontmatter\n---\nbody\n")
write(project .. "/shared.md", "---\nname: shared\ndescription: from the project layer\n---\nproject body\n")
write(project .. "/foreign.md", "---\ndescription: written for another harness\nmodel: claude-sonnet-4\n---\nbody\n")
local found = profiles.discover({ user, project })
os.execute("rm -rf " .. tmp)
return found
end
local fixture, skip_reason = discover_fixture()
local function fixture_or_skip()
if not fixture then
return nil, skip_reason
end
return fixture
end
return {
{ "frontmatter fields land on the profile", function()
local found, reason = fixture_or_skip()
if not found then
return "skip", reason
end
local reviewer = found.by_name.reviewer
assert(reviewer, "reviewer was not discovered")
assert(reviewer.name == "reviewer", "name should default to the file stem")
assert(reviewer.description == "Reviews changes", tostring(reviewer.description))
assert(reviewer.model == "anthropic:sonnet", tostring(reviewer.model))
assert(reviewer.reasoning == "high", tostring(reviewer.reasoning))
assert(reviewer.body:find("You are a reviewer.", 1, true), "body was lost")
end },
{ "discovery recurses and defaults the name to the stem", function()
local found, reason = fixture_or_skip()
if not found then
return "skip", reason
end
local planner = found.by_name.planner
assert(planner, "a nested profile was not discovered")
assert(planner.description == "", "a bodyless header means an empty description")
assert(planner.body == "You plan.\n", string.format("%q", planner.body))
assert(found.by_name["from-frontmatter"], "`name` should override the stem")
assert(found.by_name.renamed == nil, "the stem must not survive an explicit name")
end },
{ "the project layer shadows the user layer by name", function()
local found, reason = fixture_or_skip()
if not found then
return "skip", reason
end
local shared = found.by_name.shared
assert(shared, "shared was not discovered")
assert(shared.description == "from the project layer", tostring(shared.description))
assert(shared.body == "project body\n", string.format("%q", shared.body))
local count = 0
for _, profile in ipairs(found.list) do
if profile.name == "shared" then
count = count + 1
end
end
assert(count == 1, "a shadowed profile must appear once, saw " .. count)
end },
{ "a foreign model spelling warns and inherits instead", function()
local found, reason = fixture_or_skip()
if not found then
return "skip", reason
end
local foreign = found.by_name.foreign
assert(foreign, "foreign was not discovered")
assert(foreign.model == nil, "an unparseable model must be dropped")
assert(foreign.description == "written for another harness", "the rest of the header must survive")
local warned = false
for _, warning in ipairs(found.warnings) do
if warning:find("ignoring model 'claude-sonnet-4'", 1, true) then
warned = true
end
end
assert(warned, "expected a warning naming the ignored model, got: " ..
table.concat(found.warnings, " | "))
end },
{ "the list is sorted by name", function()
local found, reason = fixture_or_skip()
if not found then
return "skip", reason
end
assert(#found.list >= 5, "expected every profile in the list, saw " .. #found.list)
for index = 2, #found.list do
assert(found.list[index - 1].name < found.list[index].name,
"list is not sorted at " .. index)
end
end },
}
|