Keeping business logic out of prompts¶
Prompts accumulate pricing, refund thresholds, cancellation windows and eligibility conditions written in English. Those are business rules with none of the properties a business rule needs: untested, unversioned against the code that depends on them, and advisory — the model may simply not apply one, and nothing fails when it does not. That is how a refund rule becomes a suggestion.
The split is the kit's own: agents reason, code transacts.
| Belongs in the prompt | Belongs in code |
|---|---|
| Role and task framing | Monetary thresholds and shares of an amount |
| Tone, style, verbosity | Eligibility conditions and rule chains |
| The output contract | Time windows that govern an outcome |
| What to ask a tool for | Authorisation decisions |
| How to cite what it used | Irreversible actions |
Running it¶
from tesserix_adk.core import lint_directory
report = lint_directory(Path("prompts"))
print(report.summary())
raise SystemExit(report.exit_code)
lint_prompt(text, source=...) does the same for a prompt held inline rather than in a
directory, so a project migrating to a registry can lint what it has today.
The rules¶
| Code | What it catches | Severity |
|---|---|---|
ADK-P000 |
A suppression with no recorded justification | error |
ADK-P001 |
A monetary threshold or a share of an amount | error |
ADK-P002 |
A conditional rule chain | error |
ADK-P003 |
A time window governing an outcome | error |
ADK-P004 |
Language granting the model authority to decide | error |
ADK-P005 |
An instruction to perform an irreversible action directly | error |
ADK-P006 |
An embedded endpoint, URL or service name | warning |
Every rule carries a remedy, because a check that only says no is a check projects turn off. Warnings are reported and do not fail the run.
A worked refactor¶
Before, with the rule in the prompt:
If the booking was made more than 24 hours ago, refund 50% of the fare.
Three findings: the share of an amount, the rule chain, the time window. After, with the rule in a tool and the prompt asking it:
@tool
def refund_quote(booking_id: str) -> RefundQuote:
"""Return what this booking is entitled to, and why."""
Ask
refund_quotewhat the booking is entitled to. Tell the customer the amount and the reason it gives, and do not offer anything it did not return.
The threshold now lives beside the code that charges, has a test, and refuses when it should.
ADK-P005 pairs with the runtime's approval gate: a prompt is not an enforcement point,
and a retry of one is a second refund.
False positives, and suppression¶
Ordinary numbers pass. "Return at most 5 suggestions, in 2 sentences each" is output shaping, not a business rule, and nothing flags it — the rules key on currency, shares, time units and authorisation verbs rather than on digits.
Where a finding is genuinely wrong or genuinely accepted, suppress it with a reason:
The comment covers its own line and the line below, and only the code it names. A suppression
with no reason raises ADK-P000 in its place, which is an error, so silencing the check
costs the same as fixing it unless somebody writes down why. Every report counts its
suppressions, so a project that suppressed everything reads as 12 suppressed rather than as
green.
Known limitations¶
- The patterns are English. A tenant-specific prompt written in another language will pass a rule this would catch in English.
- A rule split across two lines, two prompts or two versions is not detected: each line is read on its own.
- Prompt text is matched, not understood. The lint is a reviewer's floor, not a proof.