-- 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 }, }