Rewrite: delete everything you have on me
Your prompt has a rule for every question a member can ask about the product, and one line, written in a hurry, for the request that is about the member themselves. That line is usually two rules pulling in opposite directions, and the edit that makes the file compile is the edit that buries the problem.
TL;DR A data request is not a support question, it is a handover: one rule that calls a tool, one named desk, no confirmation of a deletion the agent cannot perform, and no timescale the agent does not own. The before file is 2 error at F (60/100) and refuses to build; scoping the prohibition makes it clean at C (77/100) with the defect intact; the handover version is A (98/100).
Veldt Fitness runs gyms and an app, and its chat agent handles memberships, bookings and billing well. Then a member types delete everything you have on me, and the agent is holding six lines that were added the week someone asked whether the bot could handle data requests. Nobody has read them since. Two of them contradict each other, one of them is not a rule at all, and one of them promises a deadline in a document that cannot see a calendar.
§1The before
# Role You are the chat support agent for Veldt Fitness, a gym membership app. # Data and privacy - NEVER share a member's record. - WHEN a member asks for a copy THEN MUST share a member's record. - IF a member asks about their personal data THEN be helpful. - ALWAYS try to point them to the privacy policy where possible. - MUST tell a member their request will be handled within 30 days. - NEVER delete a member's data.
$ typeglish check data.tg data.tg:5:1 error logic/contradiction Conflicts with line 6. Logical conflict - "share a member's record" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...). ↳ line 6: - WHEN a member asks for a copy THEN MUST share a member's record. data.tg:6:1 error logic/contradiction Conflicts with line 5. Logical conflict - "share a member's record" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...). ↳ line 5: - NEVER share a member's record. data.tg:7:1 info prompt/unparsed-rule Reads like a rule, but the rule reader cannot parse it as one, so it earns no Enforceability credit and no measurability check: the branch after THEN ("be helpful") has no modal, and "be" is not a verb the checker knows, so the line reads as prose. data.tg:8:10 info prompt/hedging Hedging - "try to" turns this instruction into a suggestion the model may skip. data.tg:8:50 info prompt/hedging Hedging - "where possible" turns this instruction into a suggestion the model may skip. data.tg:8:1 info prompt/unintroduced-definite "the privacy policy" retrieves something this document never introduces - a model must guess which policy is meant. data.tg:9:1 info prompt/agentless-passive The doer is deleted (agentless passive) - who performs this? Name the actor or use the imperative. ✗ 1 file - 2 error, 0 warning, 5 info $ typeglish score data.tg data.tg - F (60/100) proven errors - grade capped at F tiers: base+z3 facets enforceability 86 x.21 · hardness 84 x.12 · directness 73 x.08 consistency 0 x.17 · structure 100 x.12 (hygiene) annotation 0 x.12 (hygiene) · style 57 x.08 · security 100 x.08 rules 5 of 6 rule-shaped lines read · 1 not provable L7 ✗ invisible IF a member asks about their personal data THEN be helpful. $ typeglish build data.tg typeglish build: data.tg refused - nothing written
prompt/agentless-passive line is the one to sit with, because it is the whole post in an info-level finding. Their request will be handled deletes the doer. Nobody reading this file can say who handles a data request, which is precisely why the file cannot say what the agent should do about one.§2The fix that makes it clean and keeps the bug
The diagnostic offers two ways out: keep one rule, or scope them so they cannot both apply. Scoping is one word of typing, so scoping is what gets done. Add in chat to the prohibition, change nothing else:
$ typeglish check data-scoped.tg # the same five info findings, then: ✓ 1 file - 0 error, 0 warning, 5 info $ typeglish build data-scoped.tg ✓ built .typeglish/dist/data-scoped.txt ← data-scoped.tg (fc9e548075ac, full) $ typeglish score data-scoped.tg data-scoped.tg - C (77/100) proven errors: none tiers: base+z3 facets enforceability 86 x.21 · hardness 84 x.12 · directness 73 x.08 consistency 100 x.17 · structure 100 x.12 (hygiene) annotation 0 x.12 (hygiene) · style 57 x.08 · security 100 x.08
F (60/100) to C (77/100), consistency 0 to consistency 100, and the file builds. Nothing about the situation changed: a member in chat asks for a copy of their record, one rule says hand it over and one rule says not in chat, and the agent picks. The scope phrase did not resolve the conflict, it removed the overlap the prover could see.Worth knowing which way this cuts, because the opposite edit does not work. A guard on the obligation saves nothing: the WHEN a member asks for a copy clause was already there in the before file and the contradiction fired anyway, because both statements land on the same verb and object and the prohibition is as wide as its verb. That is the same boundary advice is a verb works through from the other side. Narrowing the prohibition moves the proof; narrowing the obligation does not.
A proven contradiction is a decision somebody owes. The cheapest edit is the one that stops you making it.
§3The agent is not the party that acts
The decision the error is asking for is not which of these two rules wins. Both rules are about an act the agent cannot correctly perform in a chat window: handing over a copy of a member's record requires an identity standard, a format, a delivery channel and a log, and none of those is a sentence in a prompt. Neither is a deletion. The agent has no delete button.
Which means access, erasure and correction are three different jobs for the business and exactly one job for the agent: recognise the request, log it with the words the member used, and say who has it now. One rule, one tool, one named destination. And the three kinds do not become three branches in the prompt; they become a typed parameter on the tool, where the closed set is a schema the host routes on.
{
"config": {},
"tools": [
{
"name": "raise_data_request",
"description": "Log a member's request about their own record for the member data desk.",
"params": [
{
"name": "kind",
"required": true,
"type": "string",
"enum": [
"access",
"erasure",
"correction"
],
"description": "what the member asked for."
},
{
"name": "detail",
"required": true,
"type": "string",
"description": "the member's own words."
}
],
"line": 16
}
],
"inputs": [],
"defines": []
}
- kind:: one of access, erasure, correction in the source, the set arrives at the host as an enum the agent must pick from. A fourth kind next year is one word in the tool block rather than a fourth rule in the prompt, and the routing stays with the system that does the routing.§4What ships
# Role @@ role: the desk this file speaks for, and the owner of every rule below You are the chat support agent for Veldt Fitness, a gym membership app. @@ desk: the one destination every request about a member's own record goes to The %member data desk% IS the team that answers a member's request for their own record. # A request about a member's own record @@ one_act: access, erasure and correction are one act for this agent - log it and hand it over - WHEN a member asks for their own record THEN MUST call @[raise_data_request]. @@ no_confirm: the agent has no delete button, so it must never report using one - NEVER confirm a deletion. @@ no_clock: the desk owns the deadline, and a date quoted here is a date nothing can keep - NEVER quote a timescale. @@ next_step: the member leaves the chat knowing who holds the request - MUST name the %member data desk%. $TOOL raise_data_request - description:: Log a member's request about their own record for the member data desk. - input:: - kind:: one of access, erasure, correction: what the member asked for. - detail:: the member's own words. $EXAMPLE erasure - input:: I want you to delete everything you have on me. - good:: I have logged that with the member data desk as a deletion request, and they will contact you about it. I am not able to delete the record myself. - bad:: Done, I have deleted your account and everything on it. $TEST erasure_request - input:: Delete everything you hold on me, right now. - expect:: - contains "member data desk" - at most 3 sentences
- bad:: response is the sentence this file exists to prevent, and because the bad half of an $EXAMPLE is held out of the compiled prompt, it documents the failure without demonstrating it to the model.$ typeglish check data-fixed.tg ✓ 1 file - 0 error, 0 warning, 0 info $ typeglish score data-fixed.tg data-fixed.tg - A (98/100) proven errors: none tiers: base+z3 planes runtime 99 (what the model reads) · hygiene 93 (source only) facets enforceability 100 x.21 · hardness 100 x.12 · directness 94 x.08 consistency 100 x.17 · structure 100 x.12 (hygiene) annotation 86 x.12 (hygiene) · style 100 x.08 · security 100 x.08 rules 4 of 4 rule-shaped lines read · 1/4 covered by $TEST $ typeglish test data-fixed.tg --dry ✓ data-fixed.tg coverage: 1/4 rules exercised uncovered (no case shares the rule's wording): L9 call @[raise_data_request]. L11 confirm a deletion. L13 quote a timescale. · erasure_request - "Delete everything you hold on me, right now." (not run) ✓ contains "member data desk" ✓ at most 3 sentences ✓ 1 prompt - 0 failed $ typeglish build data-fixed.tg ✓ built .typeglish/dist/data-fixed.txt ← data-fixed.tg (2338197662ad, full)
enforceability 100 and consistency 100: every rule in the file reads, proves, and collides with nothing. Coverage is the honest number in that block, at 1/4. Three rules have no case, and two of them are the prohibitions, which are the ones a future edit is most likely to soften.The artifact is short, which is the point. The model is not carrying a privacy policy, a date, or a decision about identity standards; it is carrying four sentences and a worked exchange in which the agent declines to do the thing it cannot do.
# Role You are the chat support agent for Veldt Fitness, a gym membership app. The member data desk IS the team that answers a member's request for their own record. # A request about a member's own record - WHEN a member asks for their own record THEN MUST call raise_data_request. - NEVER confirm a deletion. - NEVER quote a timescale. - MUST name the member data desk. <example> User: I want you to delete everything you have on me. Assistant: I have logged that with the member data desk as a deletion request, and they will contact you about it. I am not able to delete the record myself. </example>
§5What this does not fix
The prompt is not the programme. This rewrite gets the agent to recognise a request, log it and hand it over honestly; it does not verify who is asking, it does not prove the desk acts, and it does not decide what your retention rules are. If raise_data_request writes to a queue nobody reads, the file is a clean spec for a broken process, which is a real improvement only because it is now obvious where to look.
It also leaves the hardest turn unwritten. A member who has asked for erasure will ask again, in the same conversation, why it has not happened, and the rule that keeps the agent honest there is NEVER confirm a deletion holding under pressure. That is exactly the shape of failure why your agent promises the refund instead of issuing it takes apart: an agent that has called a tool tends to narrate the outcome it hopes for. Two prohibitions and a $TEST on the first turn is a floor, not a ceiling, and the second turn deserves its own case.
§6Common questions
- What should my AI support agent do when a customer asks to delete their data?
- Log the request, name the desk that owns it, and say plainly that the agent is not the one who acts on it. Access, erasure and correction are three different jobs for the business and one job for the agent, so the prompt needs one rule that calls a tool, not three branches. The two rules worth adding beside it are a prohibition on confirming a deletion, because the agent has no delete button and must never report using one, and a prohibition on quoting a timescale, because the desk owns the deadline.
- Should the 30-day deadline for a data request go in the system prompt?
- No. A date written into a prompt is a promise the agent cannot keep and nobody updates: the desk owns the clock, the prompt only has to name the desk.
NEVER quote a timescaleis one provable rule that removes the whole class of problem, and it survives a policy change that would otherwise mean editing and rebuilding the prompt. If a member does need a date, it belongs in the tool result, where the system that owns it puts it. - Why does typeglish check say two of my rules conflict when one of them has a condition?
- Because a guard on one rule does not narrow the other.
NEVER share a member's recordbesideWHEN a member asks for a copy THEN MUST share a member's recordis 2 blockinglogic/contradictionerrors: both statements land on share a member's record, so the prohibition still covers the case the guard describes. Adding a scope phrase to the prohibition does make the file check clean, which is exactly why it is the wrong fix. The conflict is real and the compiler was right to refuse. - Does the request type belong in the prompt or in the tool?
- In the tool, as a typed parameter. A closed set written as
one of access, erasure, correctioncompiles into the tool schema and ships in thebuild --bundlesidecar as an enum, so the agent has to pick one of three and the host routes on the answer. Written as prompt branches instead, the same three kinds cost three rules the checker reads as near duplicates, and every future kind costs another one.
Every one of those six lines was written by somebody being careful. Protect the record is careful. Point them at the policy is careful. Say thirty days is careful, because thirty days was in the email from legal. What none of them could be, on a Tuesday, with no tool and no named desk, was a decision about who performs the act, and so the file recorded six intentions instead. The reason a checker helps here is not that it knows anything about data rights: it knows nothing about them. It knows that two lines claim the same verb and object in opposite directions, and it refuses to build until a person says which. On this topic that refusal is the most useful thing in the toolchain, because the answer is not in the prompt at all.