feat: mr-reviewer and solution-reviewer join the scaffold, so they exist in a repo
Eleven agents lived only in `~/.claude/agents/`, which is not a git repository — they would have gone with the laptop. Eight of those were product-specific reviewers already defined in the projects that need them, and they are removed from the user level rather than duplicated here. Two were genuinely generic and already product-neutral (zero references to any product, customer or employer), so they belong in the shared scaffold: `mr-reviewer` reviews open MRs/PRs on a non-primary remote in a multi-remote repository, and `solution-reviewer` does a whole-codebase health review. `irs-validator` is deliberately NOT brought in. It is personal tooling rather than a development capability, and per the user CLAUDE.md personal tooling must not be referenced inside a project repository because those are mirrored to other remotes. The client manifest gains `fetch` and `websearch` capability mappings, which `solution-reviewer` needs. Worth noting how that surfaced: the sync REFUSED to render rather than dropping the two tools silently, which is the fail-safe behaviour the capability mapping exists to provide — an unmapped capability that rendered as absent would have quietly removed the agent's web access. `~/.claude/agents/README.md` now records that ten of the eleven agents there are installed output from this repository, how to regenerate and reinstall them, and which one is deliberately personal.
This commit is contained in:
@@ -205,6 +205,15 @@ def parse_role(source: Path) -> RoleDefinition:
|
||||
mutation = metadata["mutation"]
|
||||
if mutation not in _MUTATION_POLICIES:
|
||||
raise ValueError(f"{source}: unsupported mutation policy {mutation!r}")
|
||||
# A name is an identifier and appears in paths, matrix routing and Rust
|
||||
# constants, so it stays a plain slug. A DESCRIPTION is prose and will
|
||||
# legitimately contain a colon ("problems: missing indexes on FK columns"),
|
||||
# so it is not the author's job to avoid YAML metacharacters — the renderer
|
||||
# quotes it. See `_yaml_scalar`.
|
||||
if ": " in metadata["name"]:
|
||||
raise ValueError(
|
||||
f"{source}: name must be a plain slug, got {metadata['name']!r}"
|
||||
)
|
||||
capabilities = tuple(
|
||||
item.strip() for item in metadata["capabilities"].split(",") if item.strip()
|
||||
)
|
||||
@@ -237,13 +246,37 @@ def _mapped_capabilities(role: RoleDefinition, client: ClientDefinition) -> list
|
||||
return [client.capabilities[name] for name in role.capabilities]
|
||||
|
||||
|
||||
def _yaml_scalar(value: str) -> str:
|
||||
"""Render a string as YAML that parses back to exactly this string.
|
||||
|
||||
A plain (unquoted) scalar cannot contain `: `, cannot contain ` #`, and cannot
|
||||
begin with an indicator character. HF-2026-001173 shipped a description with
|
||||
`here: ` in it, and the Rust model-policy gate — which parses frontmatter as
|
||||
real YAML — refused the file the sync had just called current.
|
||||
|
||||
Quoting rather than refusing, because a description is prose: MA's
|
||||
`data-architect` reads "…for schema-design and database-health problems:
|
||||
missing indexes on FK/filter/join columns…". Making an author reword that to
|
||||
satisfy a renderer puts the tool's limitation into the standard's wording.
|
||||
"""
|
||||
unsafe = (
|
||||
": " in value
|
||||
or " #" in value
|
||||
or value.rstrip().endswith(":")
|
||||
or value[:1] in "#&*!|>%@`'\"[]{},"
|
||||
)
|
||||
if not unsafe:
|
||||
return value
|
||||
return '"' + value.replace("\\", "\\\\").replace('"', '\\"') + '"'
|
||||
|
||||
|
||||
def _render_claude(role: RoleDefinition, client: ClientDefinition) -> str:
|
||||
tier = _resolve_tier(role, client)
|
||||
tools = _mapped_capabilities(role, client)
|
||||
lines = [
|
||||
"---",
|
||||
f"name: {role.name}",
|
||||
f"description: {role.description}",
|
||||
f"description: {_yaml_scalar(role.description)}",
|
||||
]
|
||||
if tier is None:
|
||||
# A markdown client declaring tier_policy: unsupported. Not reachable
|
||||
|
||||
Reference in New Issue
Block a user