The wording search
Three of the guardrail's checks read what the person said: S1 (a turn that asks to send something out, where the tool called sends nothing), S2 (a turn naming a destination the call does not take) and the confirm read (the call's tool read against the turn, its values compared). They read through a deterministic reader with a closed set of words. The wording search red-teams those three checks with every wording that can change their answer, and no other.
How it is run
In Python:
report = rail.search_wordings("en", checks=["s1_veto", "s2_veto", "confirm_read"], seconds=120)
In stalwart mcp: search_wordings(tools="tools.json", language="en", seconds=60).
It runs the real reader, on your machine, through the licensed runtime: no model and no network beyond it. It reads shortest wordings first, and stops at each check's time budget, saying how far it got.
How wordings are chosen: each check's cone
The reader reads a turn through its own words: your tools' words, its word files and its grammar's frame words. Every other word reaches it as one symbol, a word it does not hold. So the wordings that matter to a check are sentences over the words that check reads, its cone:
| Check | Its cone | Grows with your tools |
|---|---|---|
| S1 | the sending verbs (and one slip of each), sending one "other" thing in every frame; and each sending verb with each of your tools' things, and with each place a thing is sent to, in the bare frame | by one wording per sending verb for each new thing |
| S2 | each tool that takes no destination, asked with a destination of each shape (an e-mail address, a URL), in every frame | linearly, one set per tool |
| Confirm read | each tool, each of its parameters named by its own words and said with a value of its shape, in every frame but the negated | linearly, one set per parameter |
The frames are the grammar's own: the bare instruction, request words ("please"), framings, openers, wanting forms ("I want to"), negators ("do not") and joins ("and then"). They come from the toolkit's frame words, which the package carries as a file checked against the toolkit's own lists. Every other word is one word checked to be absent from the reader's words.
What each check must answer
- S1: a wording asking to send something out is declined by S1, or read as one of your tools that sends; a negated one may be either.
- S2: a destination given to a tool that takes none is caught.
- Confirm read: a value said for a parameter is read for it.
A wording whose answer is not the expected one is a finding, with the wording to replay through check_call.
What "exhaustive within the cone" claims
The cone is checked, not assumed. A sample of the wordings, and every finding, is read again with the "other" word replaced by other absent words, and S1's framed wordings with one of your things in its place. The answer must not change. Where it does, the report says the search is not exhaustive for that check and names the word.
S2's and the confirm read's wordings hold no "other" word: every word in them is in the cone, so there is nothing to replace, and their report says so.
A check's report is exhaustive where every wording in its cone was read within the budget and the cone's check held: within the bounds, no wording of those words gives the wrong answer.
What it cannot find
A sending verb, a destination shape or a way of naming a value that the reader does not know. Those are outside every cone, because the reader holds no word for them. Words outside the cone added to a turn are not searched, and what the reader makes of them is not claimed.
The bounds
seconds (each check's time budget; 0 is unbounded), slips (one slip of each sending verb: its last two letters
swapped), joins (an instruction joined to a reading one) and sample_every (how often a wording is re-read for the
cone's check). As a guide: on a 4-tool catalogue every cone is read in about 25 s in English and 66 s in Portuguese; on 100 tools, S1 takes
about 70 s, S2 about 3 minutes and the confirm read about 5.
Reading the report
{"language": "en", "other": "zorvak", "bounds": {"seconds": 120, "slips": 1, "joins": true, "sample_every": 25},
"tools": 4, "seconds": 18.94,
"checks": [{"check": "s1_veto", "wordings": 4320, "read": 4320, "complete": true, "exhaustive": true, "seconds": 18.937,
"findings": [{"wording": "post the note", "frame": "bare:thing", "expected": "declined_or_not_misread",
"got": {"s1": false, "misread_as": ["save_note"]}}],
"abstraction": {"checked": 423, "holds": true, "words": []}}]}
wordingsandread: the cone's size and how much was read;completewhere they are equal.exhaustive: complete, and the cone's check held.findings: each wording with the wrong answer, its frame, what was expected and what the reader did.abstraction: how many re-reads checked the cone, whether it held, and the words that broke it.