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

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": []}}]}