summaryrefslogtreecommitdiff
path: root/spec/test_frontmatter.lua
diff options
context:
space:
mode:
authort <t@tjp.lol>2026-08-16 20:42:43 -0600
committert <t@tjp.lol>2026-08-17 20:31:29 -0600
commit4f0a91ef55fe96835172bdad34feec1e2a0a0977 (patch)
treeed4f3e86575aa6243043bc22f8037be153a609c6 /spec/test_frontmatter.lua
parentc1ab34754d3f3695fafd344fe1a181ecf0740761 (diff)
subagents extension on the generic host surfaces
The rock now owns all subagent policy on top of libpanto-lua's generic APIs: children are ordinary panto.agent instances over rock-constructed stores, started with agent:run_async and awaited by arming uv.new_poll on each job's wake_fd from the tool handler's coroutine. subagents/jobs.lua carries the session policy the host used to own: the concurrency gate (4 running, FIFO queue, cancel-while-queued never starts), the await contract (results in input order; "first" returns settled plus remaining by identity), and settle-time shaping. subagents/spawn.lua seeds new children (primary system context, child role, profile body with manifest metadata), resolves model/reasoning through panto.ext.resolve_model, filters subagents.* out of the inherited tool set via agent:set_tools, and reads resume defaults back from stored message metadata. One-shot structured workers are a null_store agent with a declaration-only output tool, tool_choice forced, dispatch_tools=false. subagents/progress.lua renders per-tool-entry cards through the component handle's invalidate seam; turn_interrupt cancels live children, turn_end closes them. Spec suite rewritten against fakes of the new surfaces (98 cases), including gate/queue/cancel bounds, resume-default extraction, one-shot capture via unresolved tool calls, tool filtering, and manifest seeding.
Diffstat (limited to 'spec/test_frontmatter.lua')
-rw-r--r--spec/test_frontmatter.lua88
1 files changed, 88 insertions, 0 deletions
diff --git a/spec/test_frontmatter.lua b/spec/test_frontmatter.lua
new file mode 100644
index 0000000..3f0c4c5
--- /dev/null
+++ b/spec/test_frontmatter.lua
@@ -0,0 +1,88 @@
+-- subagents/frontmatter.lua: splitting a profile into YAML header and body.
+--
+-- The body is the part a broken header must never cost the user, so every
+-- degraded case is checked for "body preserved verbatim" as well as for the
+-- warning. Cases that actually parse YAML need lyaml and skip without it.
+
+local frontmatter = require("subagents.frontmatter")
+
+local function lyaml_or_skip()
+ return pcall(require, "lyaml")
+end
+
+return {
+ { "fenced header parses and keeps the body verbatim", function()
+ if not lyaml_or_skip() then
+ return "skip", "lyaml is not installed"
+ end
+ local body = "You are a reviewer.\n\n indented line \n"
+ local data, parsed_body, warning = frontmatter.parse(
+ "---\nname: reviewer\ndescription: Reviews changes\nmodel: anthropic:sonnet\n---\n" .. body)
+ assert(warning == nil, "unexpected warning: " .. tostring(warning))
+ assert(type(data) == "table", "expected a mapping")
+ assert(data.name == "reviewer", tostring(data.name))
+ assert(data.description == "Reviews changes")
+ assert(data.model == "anthropic:sonnet", tostring(data.model))
+ assert(parsed_body == body, string.format("body was rewritten: %q", parsed_body))
+ end },
+
+ { "CRLF fences are tolerated", function()
+ if not lyaml_or_skip() then
+ return "skip", "lyaml is not installed"
+ end
+ local data, body, warning = frontmatter.parse("---\r\nname: crlf\r\n---\r\nbody\r\n")
+ assert(warning == nil, tostring(warning))
+ assert(type(data) == "table" and data.name == "crlf", "header did not parse")
+ assert(body == "body\r\n", string.format("%q", body))
+ end },
+
+ { "no fence means the whole file is the body", function()
+ local text = "You are a reviewer.\n\n---\n\nNot a header.\n"
+ local data, body, warning = frontmatter.parse(text)
+ assert(data == nil, "expected no metadata")
+ assert(body == text, "body was rewritten")
+ assert(warning == nil, tostring(warning))
+ end },
+
+ { "an unterminated fence is treated as prose", function()
+ local text = "---\nname: never closed\n\nstill prose\n"
+ local data, body, warning = frontmatter.parse(text)
+ assert(data == nil, "expected no metadata")
+ assert(body == text, "body was rewritten")
+ assert(warning == nil, "a lone rule is not an error")
+ end },
+
+ { "an empty fenced block is an empty mapping", function()
+ local data, body, warning = frontmatter.parse("---\n---\nbody\n")
+ assert(type(data) == "table" and next(data) == nil, "expected an empty mapping")
+ assert(body == "body\n", string.format("%q", body))
+ assert(warning == nil, tostring(warning))
+ end },
+
+ { "broken YAML warns and keeps the body", function()
+ if not lyaml_or_skip() then
+ return "skip", "lyaml is not installed"
+ end
+ local data, body, warning = frontmatter.parse("---\na: [unclosed\n---\nbody\n")
+ assert(data == nil, "a broken header must not produce metadata")
+ assert(body == "body\n", string.format("%q", body))
+ assert(type(warning) == "string" and warning:find("did not parse", 1, true),
+ "expected a parse warning, got " .. tostring(warning))
+ end },
+
+ { "a non-mapping document warns and keeps the body", function()
+ if not lyaml_or_skip() then
+ return "skip", "lyaml is not installed"
+ end
+ local data, body, warning = frontmatter.parse("---\njust a string\n---\nbody\n")
+ assert(data == nil, "a scalar header must not produce metadata")
+ assert(body == "body\n", string.format("%q", body))
+ assert(type(warning) == "string" and warning:find("not a mapping", 1, true),
+ "expected a mapping warning, got " .. tostring(warning))
+ end },
+
+ { "empty input is empty output", function()
+ local data, body, warning = frontmatter.parse("")
+ assert(data == nil and body == "" and warning == nil)
+ end },
+}