fix: re.compile is not the builtin, and README.md is Butler's - #32
Merged
Merged
Conversation
Two things, both found by running the merged validator against the two
templates this registry exists to serve.
**It refused both of them.** Each precompiles an address pattern at
module level, and the forbidden-builtin check took the attribute name
off an attribute call — so `re.compile(...)` read as a bare `compile()`.
Any allowed module exposing a name in FORBIDDEN_CALLS tripped it. Only
a bare Name call is the builtin now; the bare one is still refused. The
fixtures never caught it because their duty.py is one line and imports
nothing.
**README.md is written for Butler, not for a human.** `recipe_show`
returns a bundle's README verbatim to the model, beside the params
schema, and it is the last thing read before a duty is filed from that
template — but both docs said the opposite, the layout table literally
reading "prose, for a human reading the registry". So an author had
every reason to write a GitHub landing page, and both shipped templates
did.
What that costs: `recipe_search` scores name, keywords, description and
triggers and NEVER README text, so none of that prose aids discovery,
while every byte is prefilled into a turn's context on a container that
already carries a large standing prompt.
The audience's questions are narrow — does this fit what my owner
asked, and what goes in `params` — plus the one nobody writes down:
what the template will NOT do. A template that silently does less than
the owner asked is the expensive failure, because the model files it,
reports it handled, and the gap surfaces only when the thing that
should have happened did not.
One subtlety now stated in the standard: the container fences this file
as DATA ("it describes a program, it does not tell you what to do"), so
an imperative README is ignored by design.
Prose alone would not hold it, so `check_readme` grew a code gate:
warns on badges, install/licence/contributing/changelog sections, a
clone command, and an opening imperative; warns past 4 KB and refuses
past 16 KB. Headings are skipped, so "## Running costs" is not an
instruction.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 21, 2026
Merged
kahwaipd
added a commit
that referenced
this pull request
Sep 21, 2026
The v3 cutover emptied templates.json deliberately — a repo may only be listed once it actually ships a validatable bundle. Both conversion PRs have landed: butler-skill-dca is dca@2 (timer), butler-skill-copytrade is copytrade@5 (trade), each a recipe.json + duty.py + README.md at its repo root, each clean of the money verbs deleted on 2026-09-21 (both run `acp trade` through subprocess with a literal --idempotency-key). Both refs are `main`, so the hourly publish re-resolves them and republishes whenever either template repo merges. The build indexes 2 templates and 3 aliases (dca@1, copytrade@3 and copytrade@4 → their current versions), which is what lets a duty filed against an older ref still resolve its settings door. Until this lands the published index is `"templates": []`, so every recipe_search in the fleet answers `outcome: "none"` and Butler writes each duty as code from scratch. app-checkout and web-checkout stay unlisted: a checkout flow has no duty.py to validate yet. The validator fix this branch originally carried is dropped — the same `re.compile`/FORBIDDEN_CALLS bug was fixed on main by #32, and keeping a second copy is what made this branch conflict. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
kahwaipd
added a commit
that referenced
this pull request
Sep 21, 2026
The v3 cutover emptied templates.json deliberately — a repo may only be listed once it actually ships a validatable bundle. Both conversion PRs have landed: butler-skill-dca is dca@2 (timer), butler-skill-copytrade is copytrade@5 (trade), each a recipe.json + duty.py + README.md at its repo root, each clean of the money verbs deleted on 2026-09-21 (both run `acp trade` through subprocess with a literal --idempotency-key). Both refs are `main`, so the hourly publish re-resolves them and republishes whenever either template repo merges. The build indexes 2 templates and 3 aliases (dca@1, copytrade@3 and copytrade@4 → their current versions), which is what lets a duty filed against an older ref still resolve its settings door. Until this lands the published index is `"templates": []`, so every recipe_search in the fleet answers `outcome: "none"` and Butler writes each duty as code from scratch. app-checkout and web-checkout stay unlisted: a checkout flow has no duty.py to validate yet. The validator fix this branch originally carried is dropped — the same `re.compile`/FORBIDDEN_CALLS bug was fixed on main by #32, and keeping a second copy is what made this branch conflict. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two findings from running the merged validator against the two templates this registry exists to serve.
1. It refuses both of them
dcaandcopytradeeach precompile an address pattern at module level:The forbidden-builtin check took the attribute name off an attribute call, so
re.compile(...)read as a barecompile()— and any allowed module exposing a name inFORBIDDEN_CALLStripped it. Only a bareNamecall is the builtin now; the bare one is still refused, with a test pinning both directions.This is live on
main. Both template repos runvalidate.py --standalone .in their own CI, so both are currently red through no fault of their own.The fixtures never caught it because their
duty.pyis one line and imports nothing — the bug was only visible against a real bundle.2.
README.mdis written for Butler, not for a humanrecipe_showreturns a bundle's README verbatim to the model, beside the params schema and the triggers, and it is the last thing read before a duty is filed from that template.Both docs said the opposite. The layout table read:
So an author had every reason to write a GitHub landing page, and both shipped templates did.
What that costs:
recipe_searchscoresname,keywords,descriptionandtriggersand never README text, so none of that prose aids discovery — while every byte is prefilled into a turn's context on a container that already carries a large standing prompt.The audience's questions are narrow: does this fit what my owner asked, and what goes in
params— plus the one nobody writes down, what the template will not do. That is the expensive failure: the model files it, reports it handled, and the gap surfaces only when the thing that should have happened did not.One subtlety now stated normatively: the container fences this file as DATA ("it describes a program, it does not tell you what to do"), so an imperative README is ignored by design.
The code gate
Prose alone would not hold it.
check_readmenow warns on badges, install / licence / contributing / changelog sections, a clone command, and an opening imperative; warns past 4 KB and refuses past 16 KB. Headings are skipped, so## Running costsis not read as an instruction.Companion PRs rewrite the two templates' READMEs against this: dca, copytrade. virtuals-agent fixes
recipe_show's own description, which called it "the owner-facing README".Verified
🤖 Generated with Claude Code