docs(issue-42): stages 1-2 - SKILL.md code-block inventory as test data; remove blocks the scripts already do

Stage 1: tests/data/skill_code_blocks.json lists all 37 fenced blocks of the
eight SKILL.md files at base 45d1621 (indented fences count), classed (a)
duplicate of a script, (b) procedure no script offers, (c) example/call/
diagram, with script file:line evidence and recorded mismatches.
tests/test_issue42_skill_blocks.py fails when a block changes without the
inventory, an (a) block comes back, a removed block's entry call is missing,
an exit-code table drifts from the script, or evidence points nowhere.

Stage 2: the five (a) blocks are gone - resume Workflow, stop pre-flight,
create pre-flight / Workflow / manual YAML append - replaced by the
entry-point call with option and exit-code tables. Each exit table has a
catch-all row: under set -e a failing sub-step's code passes through.
create's exit 4 is real (the YAML write raises SystemExit(4) for a duplicate
running row) and is listed as such. Doc-to-script mismatches are fixed in the
doc (resume Case 3 / no attach, stop --purge-conversation --yes, status
--json keys, create cmd_full and the nonexistent yaml-edit script). No
behaviour change, no version bump.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
GodopuandClaude Opus 5.5 committed 2026-10-03 13:42:08 +00:00
1 parent 45d1621c93
commit 7670b41afd
6 files changed
+901 -226

No files matched your search

+621
View File
@@ -0,0 +1,621 @@
{
"base": "45d1621",
"blocks": [
{
"skill": "multi-agent-mux-create",
"start": 63,
"end": 66,
"lang": "bash",
"sha": "e44d91b9be46",
"class": "c",
"kind": "example",
"evidence": [
"multi-agent-mux-create/scripts/create_session.sh:99",
"multi-agent-mux-create/scripts/create_session.sh:356"
],
"mismatch": null,
"note": "HERDR_SESSION_NAME environment example"
},
{
"skill": "multi-agent-mux-create",
"start": 68,
"end": 70,
"lang": "bash",
"sha": "dd27e3eccbbe",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-create/scripts/create_session.sh:82-83"
],
"mismatch": null,
"note": "--herdr-session / --herdr-workspace example"
},
{
"skill": "multi-agent-mux-create",
"start": 73,
"end": 75,
"lang": "bash",
"sha": "7c8f836dd518",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-create/scripts/create_session.sh:84"
],
"mismatch": null,
"note": "--submit-job example"
},
{
"skill": "multi-agent-mux-create",
"start": 78,
"end": 80,
"lang": "bash",
"sha": "8a5226fbc7a0",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-create/scripts/create_session.sh:85"
],
"mismatch": null,
"note": "--onboard example"
},
{
"skill": "multi-agent-mux-create",
"start": 127,
"end": 145,
"lang": "yaml",
"sha": "2dd608d04ae0",
"class": "c",
"kind": "example",
"evidence": [
"multi-agent-mux-create/scripts/create_session.sh:518-545"
],
"mismatch": null,
"note": "Output format: the row create_session.sh writes"
},
{
"skill": "multi-agent-mux-create",
"start": 151,
"end": 153,
"lang": "bash",
"sha": "613003708f19",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-create/scripts/create_session.sh:29-52"
],
"mismatch": null,
"note": "Workflow: the entry point (replaces the removed Workflow block, base 168-220)"
},
{
"skill": "multi-agent-mux-create",
"start": 191,
"end": 195,
"lang": "bash",
"sha": "38836d31bf38",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-create/scripts/worktree_prepare.sh:24",
"multi-agent-mux-create/scripts/worktree_prepare.sh:250"
],
"mismatch": null,
"note": "worktree_prepare.sh call"
},
{
"skill": "multi-agent-mux-create",
"start": 217,
"end": 239,
"lang": "bash",
"sha": "d90cfd2555fd",
"class": "b",
"kind": "verification",
"evidence": [],
"mismatch": null,
"note": "post-create checks (herdr alive, cmd/cwd, YAML row, TUI); no script offers them as a command - stage 3"
},
{
"skill": "multi-agent-mux-delegate-job",
"start": 36,
"end": 81,
"lang": "bash",
"sha": "9fcfb4503a40",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-delegate-job/multi-agent-mux-delegate-job:1236-1247"
],
"mismatch": null,
"note": "CLI reference; subcommands submit/status/list/verify/wait/logs match main()"
},
{
"skill": "multi-agent-mux-loop",
"start": 50,
"end": 53,
"lang": "bash",
"sha": "cbc86a648420",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-loop/scripts/run_loop.sh:45-46"
],
"mismatch": null,
"note": "environment + run_loop.sh call"
},
{
"skill": "multi-agent-mux-loop",
"start": 70,
"end": 93,
"lang": "-",
"sha": "7f11fedca5f9",
"class": "c",
"kind": "diagram",
"evidence": [],
"mismatch": null,
"note": "role diagram"
},
{
"skill": "multi-agent-mux-loop",
"start": 121,
"end": 168,
"lang": "mermaid",
"sha": "093c1673b128",
"class": "c",
"kind": "diagram",
"evidence": [],
"mismatch": null,
"note": "sequence diagram (mermaid)"
},
{
"skill": "multi-agent-mux-loop",
"start": 228,
"end": 234,
"lang": "bash",
"sha": "1b75c515c99a",
"class": "c",
"kind": "invocation",
"evidence": [
"lib_py/loop/runner.py:163",
"multi-agent-mux-create/scripts/worktree_prepare.sh:250"
],
"mismatch": null,
"note": "--worktree/--base --review-only call pattern"
},
{
"skill": "multi-agent-mux-loop",
"start": 240,
"end": 265,
"lang": "bash",
"sha": "9b061da667f8",
"class": "c",
"kind": "invocation",
"evidence": [
"lib_py/loop/runner.py:1787"
],
"mismatch": null,
"note": "three call examples; every flag is parsed by runner.py"
},
{
"skill": "multi-agent-mux-monitor",
"start": 60,
"end": 66,
"lang": "bash",
"sha": "d6a74c6482a9",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-monitor/scripts/reconcile.sh:48"
],
"mismatch": null,
"note": "reconcile.sh --subscribe call"
},
{
"skill": "multi-agent-mux-monitor",
"start": 109,
"end": 115,
"lang": "bash",
"sha": "55a7f7ec1a57",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-monitor/scripts/reconcile.sh:45-47"
],
"mismatch": null,
"note": "--once --emit-diff [--dry-run] call"
},
{
"skill": "multi-agent-mux-monitor",
"start": 128,
"end": 133,
"lang": "-",
"sha": "e80f013a7fc2",
"class": "c",
"kind": "example",
"evidence": [
"multi-agent-mux-monitor/scripts/reconcile.sh:601"
],
"mismatch": null,
"note": "drift A example"
},
{
"skill": "multi-agent-mux-monitor",
"start": 144,
"end": 150,
"lang": "-",
"sha": "4924c8c79db5",
"class": "c",
"kind": "example",
"evidence": [
"multi-agent-mux-monitor/scripts/reconcile.sh:708"
],
"mismatch": null,
"note": "drift B example"
},
{
"skill": "multi-agent-mux-monitor",
"start": 156,
"end": 162,
"lang": "-",
"sha": "3f0b027ff51e",
"class": "c",
"kind": "example",
"evidence": [
"multi-agent-mux-monitor/scripts/reconcile.sh:735"
],
"mismatch": null,
"note": "drift C example (indented fence)"
},
{
"skill": "multi-agent-mux-monitor",
"start": 182,
"end": 210,
"lang": "markdown",
"sha": "3f5d82c08fd9",
"class": "b",
"kind": "procedure",
"evidence": [
"multi-agent-mux-monitor/scripts/reconcile.sh:41"
],
"mismatch": [
"M1: the spec says `sleep 30`; reconcile.sh polls every RECONCILE_POLL_INTERVAL, default 15"
],
"note": "agent-run polling loop; \"report only new drifts\" and the Hard rules are agent-side guidance - stage 3"
},
{
"skill": "multi-agent-mux-monitor",
"start": 229,
"end": 233,
"lang": "bash",
"sha": "b3588563316e",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-monitor/scripts/reconcile.sh:45-46"
],
"mismatch": null,
"note": "one-shot run piped to json.tool"
},
{
"skill": "multi-agent-mux-orc-onboard",
"start": 22,
"end": 24,
"lang": "bash",
"sha": "6e26222260bb",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-orc-onboard/scripts/orc_onboard.sh:20-22"
],
"mismatch": null,
"note": "entry point call"
},
{
"skill": "multi-agent-mux-orc-onboard",
"start": 40,
"end": 46,
"lang": "bash",
"sha": "1be6e95c82c7",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-orc-onboard/scripts/orc_onboard.sh:48-62"
],
"mismatch": null,
"note": "--remove --purge-conversation --yes call + output"
},
{
"skill": "multi-agent-mux-resume",
"start": 59,
"end": 61,
"lang": "bash",
"sha": "5fe43f9fb460",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-resume/scripts/resume_session.sh:10-21"
],
"mismatch": null,
"note": "Workflow: the entry point (replaces the removed Workflow block, base 59-118)"
},
{
"skill": "multi-agent-mux-resume",
"start": 103,
"end": 124,
"lang": "bash",
"sha": "f4bddf5ba4d1",
"class": "b",
"kind": "verification",
"evidence": [],
"mismatch": null,
"note": "post-resume checks (herdr agent get, YAML, screen); no script offers them as a command - stage 3"
},
{
"skill": "multi-agent-mux-status",
"start": 30,
"end": 34,
"lang": "bash",
"sha": "ed6251410f1f",
"class": "b",
"kind": "procedure",
"evidence": [
"multi-agent-mux-status/scripts/status.sh:22"
],
"mismatch": null,
"note": "pre-flight: the YAML check duplicates status.sh:22, but status.sh has no has_real_herdr check (it reports dead without herdr) - mixed, kept"
},
{
"skill": "multi-agent-mux-status",
"start": 44,
"end": 46,
"lang": "bash",
"sha": "27cab128411c",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-status/scripts/status.sh:20"
],
"mismatch": null,
"note": "entry point call"
},
{
"skill": "multi-agent-mux-status",
"start": 61,
"end": 70,
"lang": "-",
"sha": "7e5ec388a64a",
"class": "c",
"kind": "example",
"evidence": [
"multi-agent-mux-status/scripts/status.sh:411"
],
"mismatch": null,
"note": "table output example"
},
{
"skill": "multi-agent-mux-status",
"start": 74,
"end": 106,
"lang": "json",
"sha": "cfea3a10dc8d",
"class": "c",
"kind": "example",
"evidence": [
"multi-agent-mux-status/scripts/status.sh:31-36",
"multi-agent-mux-status/scripts/status.sh:209-232",
"multi-agent-mux-monitor/scripts/reconcile.sh:1084"
],
"mismatch": [
"S1 (fixed): the example used rows/yaml_entries/unregistered; the real keys are reconcile.sh's drift object plus sessions_detail"
],
"note": "--json output example"
},
{
"skill": "multi-agent-mux-stop",
"start": 42,
"end": 55,
"lang": "bash",
"sha": "27ebf150b92a",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-stop/scripts/stop_session.sh:1-33"
],
"mismatch": [
"T2 (fixed): the --purge-conversation example lacked --yes and would exit 3"
],
"note": "Workflow: three entry point calls"
},
{
"skill": "multi-agent-mux-stop",
"start": 81,
"end": 85,
"lang": "-",
"sha": "a2b56ac238ae",
"class": "c",
"kind": "diagram",
"evidence": [
"multi-agent-mux-stop/scripts/stop_session.sh:184-198"
],
"mismatch": null,
"note": "state machine"
},
{
"skill": "multi-agent-mux-stop",
"start": 102,
"end": 104,
"lang": "bash",
"sha": "4189d73d6896",
"class": "c",
"kind": "invocation",
"evidence": [
"multi-agent-mux-stop/scripts/worktree_remove.sh:35-36"
],
"mismatch": null,
"note": "worktree_remove.sh call"
},
{
"skill": "multi-agent-mux-stop",
"start": 127,
"end": 145,
"lang": "bash",
"sha": "bcaa2a03484e",
"class": "b",
"kind": "verification",
"evidence": [],
"mismatch": null,
"note": "post-stop checks; no script offers them as a command - stage 3"
}
],
"removed": [
{
"skill": "multi-agent-mux-resume",
"base_start": 59,
"base_end": 118,
"lang": "bash",
"base_sha": "63abe1bb3542",
"class": "a",
"evidence": [
"multi-agent-mux-resume/scripts/resume_session.sh:111-130",
"multi-agent-mux-resume/scripts/resume_session.sh:133-144",
"multi-agent-mux-resume/scripts/resume_session.sh:147-158",
"multi-agent-mux-resume/scripts/resume_session.sh:172-197",
"multi-agent-mux-resume/scripts/resume_session.sh:237",
"multi-agent-mux-resume/scripts/resume_session.sh:241",
"multi-agent-mux-resume/scripts/resume_session.sh:247-250",
"lib_py/agents/adapters/claude.py:109"
],
"mismatch": [
"R1: an already-running session is exec-attached in the doc; the script only updates the row and exits 0 (no attach, no permission-flag check, #45)",
"R2: the script never attaches; attach is a separate human step",
"R3: steps missing from the doc: original-cwd check, control-plane binding, binary validation, whitespace refusal, --dry-run",
"R4: $CLAUDE_ID_FLAG is undefined; the script uses adapter resume-spec (table fallback); no -x 140 -y 40 on resume",
"R5: the script passes --agent --workspace --herdr-session --cmd-full [--herdr-workspace] to update_yaml_resumed.sh"
],
"replacement": "bash .agents/skills/multi-agent-mux-resume/scripts/resume_session.sh --workspace <path> --agent <agent> --session <name>",
"script": "multi-agent-mux-resume/scripts/resume_session.sh",
"note": "Workflow re-wrote resume_session.sh in bash"
},
{
"skill": "multi-agent-mux-stop",
"base_start": 38,
"base_end": 63,
"lang": "bash",
"base_sha": "a7ed3e902cae",
"class": "a",
"evidence": [
"multi-agent-mux-stop/scripts/stop_session.sh:99",
"multi-agent-mux-stop/scripts/stop_session.sh:140-143",
"multi-agent-mux-stop/scripts/stop_session.sh:184-188"
],
"mismatch": [
"T1: the doc offered --force-no-yaml, which no script accepts"
],
"replacement": "bash .agents/skills/multi-agent-mux-stop/scripts/stop_session.sh",
"script": "multi-agent-mux-stop/scripts/stop_session.sh",
"note": "pre-flight (registered? already stopped?); replaced by one prose line, the Workflow block already calls the entry point"
},
{
"skill": "multi-agent-mux-create",
"base_start": 35,
"base_end": 52,
"lang": "bash",
"base_sha": "4d9c56a7bd7e",
"class": "a",
"evidence": [
"multi-agent-mux-create/scripts/create_session.sh:111",
"multi-agent-mux-create/scripts/create_session.sh:114",
"multi-agent-mux-create/scripts/create_session.sh:115",
"multi-agent-mux-create/scripts/create_session.sh:117-156"
],
"mismatch": [
"the doc checks both claude and agy; the script checks only the selected agent (the script is right)"
],
"replacement": "bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh --workspace <path> --agent <agent> --role <role>",
"script": "multi-agent-mux-create/scripts/create_session.sh",
"note": "pre-flight; replaced by prose, the has_real_herdr vs command -v pitfall kept as prose"
},
{
"skill": "multi-agent-mux-create",
"base_start": 168,
"base_end": 220,
"lang": "bash",
"base_sha": "00502b0bc2f4",
"class": "a",
"evidence": [
"multi-agent-mux-create/scripts/create_session.sh:166-168",
"multi-agent-mux-create/scripts/create_session.sh:207",
"multi-agent-mux-create/scripts/create_session.sh:259-274",
"multi-agent-mux-create/scripts/create_session.sh:361-398",
"multi-agent-mux-create/scripts/create_session.sh:410-412"
],
"mismatch": [
"C1: an existing session is exit 1 in the doc, exit 3 in the script",
"C2: the doc sleeps 6 s; the script waits with wait_for_tui_ready and exits 42/43",
"C3: the doc hard-codes per-agent commands and starts claude without flags; the script uses adapter spawn-spec"
],
"replacement": "bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh --workspace <path> --agent <agent> --role <role>",
"script": "multi-agent-mux-create/scripts/create_session.sh",
"note": "Workflow re-wrote create_session.sh in bash"
},
{
"skill": "multi-agent-mux-create",
"base_start": 226,
"base_end": 252,
"lang": "yaml",
"base_sha": "f71be0f26fb8",
"class": "a",
"evidence": [
"multi-agent-mux-create/scripts/create_session.sh:491-610"
],
"mismatch": [],
"replacement": "bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh --workspace <path> --agent <agent> --role <role>",
"script": "multi-agent-mux-create/scripts/create_session.sh",
"note": "manual \"append this row\" step; the script writes the row. The lib.sh shim note (base 248-251) moved to prose under Output format"
}
],
"entry_points": {
"multi-agent-mux-resume/scripts/resume_session.sh": {
"skill": "multi-agent-mux-resume",
"exit_codes": [
"0",
"1",
"2",
"other"
],
"passthrough": {}
},
"multi-agent-mux-stop/scripts/stop_session.sh": {
"skill": "multi-agent-mux-stop",
"exit_codes": [
"0",
"1",
"2",
"3",
"4",
"other"
],
"passthrough": {}
},
"multi-agent-mux-create/scripts/create_session.sh": {
"skill": "multi-agent-mux-create",
"exit_codes": [
"0",
"1",
"2",
"3",
"4",
"42",
"43",
"other"
],
"passthrough": {
"4": "multi-agent-mux-create/scripts/create_session.sh:515"
}
}
},
"prose_mismatches": [
"C4: create named a nonexistent `agent-sessions-yaml-edit` script; removed (create_session.sh writes the row)",
"C5: create's cmd_full table (claude = `claude`) disagreed with adapter spawn-spec; replaced by \"decided by spawn-spec\"",
"C6: create_session.sh header lists exit 4 \"YAML append failure\"; there is no literal `exit 4`, but the YAML write raises SystemExit(4) for a duplicate running row (create_session.sh:515) and it passes through, so 4 IS a real exit code (the plan assumed it was not)",
"R6: resume's \"The script above re-passes them\" and \"as done in Workflow above\" pointed at the removed block; reworded (same for create's Verification note)"
]
}
+176
View File
@@ -0,0 +1,176 @@
"""Issue #42 stages 1-2: the SKILL.md code-block inventory is data, and the docs follow it.
tests/data/skill_code_blocks.json lists every fenced block of the eight
SKILL.md files with its class - (b) a procedure no script offers yet, (c) an
example, call or diagram - and the (a) blocks removed because the entry script
already does them. These tests fail when a SKILL.md block changes without the
inventory, when an (a) block comes back, or when an exit-code table drifts from
the entry script.
"""
import hashlib
import json
import re
from pathlib import Path
import pytest
REPO = Path(__file__).resolve().parent.parent
SKILLS = REPO / ".agents" / "skills"
DATA = json.loads((Path(__file__).parent / "data" / "skill_code_blocks.json").read_text(encoding="utf-8"))
SKILL_NAMES = sorted(p.parent.name for p in SKILLS.glob("multi-agent-mux-*/SKILL.md"))
KINDS = {"invocation", "example", "diagram", "procedure", "verification"}
_OPEN = re.compile(r"^(\s*)(`{3,}|~{3,})\s*([^`\s]*)")
def parse_blocks(text):
"""Fenced blocks as (start, end, lang, sha, body); 1-based fence lines.
Indented fences (inside list items) count: they render as code blocks too.
A fence closes on a line of the same character, at least as long."""
lines = text.split("\n")
out, i = [], 0
while i < len(lines):
m = _OPEN.match(lines[i])
if not m:
i += 1
continue
fence = m.group(2)
close = re.compile(r"^\s*" + re.escape(fence[0]) + "{%d,}\\s*$" % len(fence))
j = i + 1
while j < len(lines) and not close.match(lines[j]):
j += 1
body = "\n".join(lines[i + 1:j])
out.append((i + 1, j + 1, m.group(3) or "-", hashlib.sha256(body.encode()).hexdigest()[:12], body))
i = j + 1
return out
def skill_text(skill):
return (SKILLS / skill / "SKILL.md").read_text(encoding="utf-8")
_HEREDOC = re.compile(r"(?<!<)<<-?\s*(['\"]?)(\w+)\1")
_EXIT = re.compile(r"(?<![\w.])exit\s+(\d+)\b")
def literal_exit_codes(script):
"""Codes the entry script exits with literally: `exit N` in shell context.
Only the entry file itself (not lib.sh or sub-scripts); heredoc bodies and
comment lines are skipped, and `sys.exit(N)` never matches."""
codes, pending = set(), []
for line in (SKILLS / script).read_text(encoding="utf-8").split("\n"):
if pending:
if line.strip() == pending[0]:
pending.pop(0)
continue
if re.match(r"^\s*#", line):
continue
codes.update(_EXIT.findall(line))
pending.extend(m.group(2) for m in _HEREDOC.finditer(line))
return codes
def exit_table(skill):
"""First-column values of the SKILL.md `| Exit | Meaning |` table, in order."""
lines = skill_text(skill).split("\n")
heads = [i for i, l in enumerate(lines) if re.match(r"^\|\s*Exit\s*\|\s*Meaning\s*\|", l)]
assert len(heads) == 1, f"{skill}: expected one '| Exit | Meaning |' table, found {len(heads)}"
rows = []
for l in lines[heads[0] + 2:]:
if not l.startswith("|"):
break
rows.append(l.split("|")[1].strip())
return rows
def _evidence_line(ref):
path, rng = ref.rsplit(":", 1)
first, _, last = rng.partition("-")
return SKILLS / path, int(first), int(last or first)
# 1. The inventory and the docs are the same.
@pytest.mark.parametrize("skill", SKILL_NAMES)
def test_inventory_matches_skill_md(skill):
actual = [b[:4] for b in parse_blocks(skill_text(skill))]
listed = [(b["start"], b["end"], b["lang"], b["sha"]) for b in DATA["blocks"] if b["skill"] == skill]
assert listed == actual, (
f"{skill}/SKILL.md blocks differ from tests/data/skill_code_blocks.json.\n"
f"actual (start, end, lang, sha): {json.dumps(actual)}")
def test_inventory_covers_eight_skills():
assert len(SKILL_NAMES) == 8
assert {b["skill"] for b in DATA["blocks"]} == set(SKILL_NAMES)
# 2. No (a) block is left.
def test_no_duplicate_block_left():
for b in DATA["blocks"]:
assert b["class"] in ("b", "c"), b
assert b["kind"] in KINDS, b
for r in DATA["removed"]:
assert r["class"] == "a", r
# 3. A removed block is replaced by the entry-point call.
@pytest.mark.parametrize("removed", DATA["removed"], ids=lambda r: f"{r['skill']}:{r['base_start']}")
def test_removed_block_replaced_by_entry_call(removed):
assert (SKILLS / removed["script"]).is_file(), removed["script"]
assert removed["replacement"].startswith("bash .agents/skills/" + removed["script"])
bodies = [b[4] for b in parse_blocks(skill_text(removed["skill"]))]
assert any(removed["replacement"] in body for body in bodies), \
f"{removed['skill']}: no block contains {removed['replacement']!r}"
assert removed["script"] in DATA["entry_points"]
# 4. Exit-code tables do not contradict the code.
@pytest.mark.parametrize("script", sorted(DATA["entry_points"]))
def test_exit_table_agrees_with_script(script):
ep = DATA["entry_points"][script]
listed = ep["exit_codes"]
assert exit_table(ep["skill"]) == listed, f"{ep['skill']}/SKILL.md exit table rows != data"
assert listed[-1] == "other", "the table needs a catch-all row: under set -e a failing step's code passes through"
numeric = set(listed[:-1])
literal = literal_exit_codes(script)
assert literal <= numeric, f"{script} exits with {sorted(literal - numeric)} but the table lacks them"
passthrough = ep["passthrough"]
assert numeric <= literal | set(passthrough), \
f"table codes {sorted(numeric - literal - set(passthrough))} come from nowhere in {script}"
for code, ref in passthrough.items():
path, first, _ = _evidence_line(ref)
line = path.read_text(encoding="utf-8").split("\n")[first - 1]
assert re.search(rf"SystemExit\({code}\)|\bexit\s+{code}\b", line), f"{ref} does not raise {code}: {line!r}"
def test_literal_exit_rule_on_base_scripts():
"""The counting rule: heredocs and comments skipped, so create's header `exit 4`
and stop's `sys.exit(7)` are not counted."""
assert literal_exit_codes("multi-agent-mux-resume/scripts/resume_session.sh") == {"0", "1", "2"}
assert literal_exit_codes("multi-agent-mux-stop/scripts/stop_session.sh") == {"0", "1", "2", "3", "4"}
assert literal_exit_codes("multi-agent-mux-create/scripts/create_session.sh") == {"0", "1", "2", "3", "42", "43"}
# 5. Evidence exists.
def test_evidence_points_into_existing_files():
for b in DATA["blocks"] + DATA["removed"]:
for ref in b["evidence"]:
path, first, last = _evidence_line(ref)
assert path.is_file(), ref
assert 1 <= first <= last <= len(path.read_text(encoding="utf-8").split("\n")), ref
# 6. The loading path is unchanged.
@pytest.mark.parametrize("skill", SKILL_NAMES)
def test_frontmatter_name_matches_directory(skill):
m = re.search(r"^name:\s*(\S+)\s*$", skill_text(skill).split("---")[1], re.M)
assert m and m.group(1) == skill
def test_claude_skills_link_resolves_to_agents_skills():
link = REPO / ".claude" / "skills"
if not link.exists():
pytest.skip(".claude/skills is untracked; absent in a worktree")
assert link.resolve() == SKILLS.resolve()