> Markdown rendition of https://typeglish.dev/blog/agent-two-names ("Your agent has two names - Tidelines"). Canonical page: https://typeglish.dev/blog/agent-two-names · All pages: https://typeglish.dev/llms.txt

[← Tidelines](https://typeglish.dev/blog)/ Deep dives Aug 21, 2026

# Your agent has two names

A rebrand, a template, and a persona block written by somebody else. Three names for one bot, in one file, and a green check, because most of the ways you can write an agent's identity never land in the same place.

by **TypeGlish team** 8 min read #deep-dives

One bot. Three names. One tick.

**TL;DR** A possessive binds a property of its owner, so *Your name is Dana* writes to `you.name` and nothing else in the file writes there: *Name IS Aria*, *You are Aria* and *The assistant's name is Aria* all check at 0 error, 0 warning, 0 info beside it, while a second *Your name is* is 2 blocking `logic/contradiction` errors naming `single-valued "you.name"`. The pointer is the test that answers: after *Your name is Dana*, `@{you.name}` compiles to Dana, and after *You are Aria* the same pointer stays clean and ships `{you.name}` to the model.

Nobody sits down and gives a support bot three names. What happens is that the bot gets one name at launch, marketing changes it eleven months later, and the change lands in the line whoever did it happened to be looking at. The other two lines are in a section nobody opens. All three ship, together, in the document the model reads, and the model picks.

The reason a review misses it is that the three lines do not look like the same claim. *You are Aria* reads as the role. *Your name is Dana* reads as the name. *The assistant's name is Northbeam Assistant* reads as the formal one for the disclosure. To a person that is three registers of one fact. To the compiler it is three facts about three different things, which is why none of them is ever compared with the others.

## §1 Three names, and a tick

Here is the top of a chat support prompt for a mobile network, in the state it reaches you after one rebrand.

names.tg - as inherited ✗ B (84/100)

```typeglish
# Role
You are Aria, a customer support agent for Northbeam Mobile.

# Persona
Your name is Dana.
The assistant's name is Northbeam Assistant.

# Constraints
- You MUST introduce yourself by name in your first reply.
- You MUST keep every reply to at most 4 sentences.
```

Line 2 is the launch name. Line 5 is what the persona block still says, because the persona block was copied from the pilot bot and never revisited. Line 6 is the formal one legal asked for. The rule underneath tells the model to use one of them.

tg check names.tg - output

```
✓ 1 file — 0 error, 0 warning, 0 info
```

Nothing. Not a warning, not an info, and `check --strict` has nothing to escalate because there is no finding to escalate.

The score is the usual second opinion, and `consistency` is the facet whose whole job is measuring the rules against each other.

tg score names.tg - output

```
names.tg — B (84/100)  proven errors: none  tiers: base+z3
  planes  runtime 96 (what the model reads) · hygiene 50 (source only)
  facets  enforceability 85 x.21 · hardness 100 x.12 · directness 100 x.08 · consistency 100 x.17
          structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 100 x.08 · security 100 x.08
  lever   annotation 0/100 (up to +12 overall) — Put a "@@ why" note directly above each
          statement ("@@ name: why" also names it).
```

`consistency 100` on a file with three names in it. The only complaint the score has is that nobody wrote any `@@` notes. B (84/100) ships.

And the build settles what the model gets, which is all three of them, side by side, in one paragraph.

tg build names.tg - the artifact

```
$ npx typeglish build names.tg
✓ built .typeglish/dist/names.txt ← names.tg (eb63621adb81, full)

# Role
You are Aria, a customer support agent for Northbeam Mobile.

# Persona
Your name is Dana. The assistant's name is Northbeam Assistant.

# Constraints
- You MUST introduce yourself by name in your first reply.
- You MUST keep every reply to at most 4 sentences.
```

Two of the three names end up on the same physical line of the artifact, which is the clearest possible picture of the problem and still produced no diagnostic. *Introduce yourself by name* now has three candidates and no tiebreak.

## §2 The owner is the address

A possessive does not declare an object. It declares a *property of an owner*, and the pair is the address: *Your name is Dana* writes the value `dana` to the slot `you.name`. Anything that writes to a different slot is a different claim, and two different claims are not a conflict however much they look like one.

Six files, identical except for the line under *Your name is Dana*, each one a way somebody has actually written an agent's identity.

tg check owner/ - six ways to name a bot

```
owner/possessive.tg:3:1  error  logic/contradiction  Conflicts with line 4. Contradiction — single-valued "you.name" is assigned 2 different values ("dana", "aria"). Keep one, or hold several with a list ("… is dana and aria").
owner/possessive.tg:4:1  error  logic/contradiction  Conflicts with line 3. Contradiction — single-valued "you.name" is assigned 2 different values ("dana", "aria"). Keep one, or hold several with a list ("… is dana and aria").

✗ 6 files — 2 error, 0 warning, 0 info
program: 6 independent files — no $IMPORT compositions
```

Every file carries *Your name is Dana.* and one second line. One of the six is reported. `bare.tg` says *Name IS Aria.* `copula.tg` says *You are Aria.* `assistant.tg` says *The assistant's name is Aria.* `agent.tg` says *The agent's name is Aria.* `bot.tg` says *The bot IS Aria.* None of the five appears above.

Read that as a map rather than a scoreboard and it is coherent, which is the uncomfortable part.

- **The same possessive on the same owner is caught, hard.** Two `Your name is` lines are 2 blocking `logic/contradiction` errors, one report per participating line, and the message quotes the slot rather than either sentence: `single-valued "you.name"`. The file is capped at F (50/100). This is the one shape of the bug that a review does not need, because the build refuses.

- **A bare declaration is a different object.* Name IS Aria* binds `name`, full stop, with no owner in front of it. It is a perfectly good declaration and it has nothing to do with yours.

- **The role sentence is not a name.* You are Aria* is the most common way a persona gets written and it registers a claim about you, not a value in `you.name`. That distinction is invisible in the prose and decisive in the file, and §3 is how you see it.

- **A third-person owner is a third party.* The assistant's name*, *The agent's name* and *The bot* each introduce an owner the file has not connected to *you*. Every one of them reads, to a person, as the same bot. Every one of them is a separate address, which is the same failure that makes [half your rules about somebody else](https://typeglish.dev/blog/one-subject-per-prompt) exempt from comparison with the other half.

> Two claims are only in conflict if they were about the same thing. Grammar decides what the same thing is.

## §3 The pointer is the question that gets an answer

You cannot read a slot off the page, and you should not try. There is a mechanical way to ask what a line bound, and it takes one edit: point at it. A pointer resolves against what was actually declared, so it either inlines a value or it fails.

tg check ptr/ - four files, one pointer each

```
ptr/bare-name.tg:6:30  error  structure/undefined-ref  Dangling pointer — @{name} names nothing. Declare it with "$REQUIRE variable name" (a runtime variable), or "name IS …" (a fixed value).

✗ 4 files — 1 error, 0 warning, 0 info
program: 4 independent files — no $IMPORT compositions
```

All four files carry the rule *You MUST greet a caller as \<pointer> in your first reply.* `you-name.tg` declares *Your name is Dana* and points at `@{you.name}`: clean. `assistant-name.tg` declares *The assistant's name is Aria* and points at `@{assistant.name}`: clean, and it is a different owner. `bare-name.tg` declares *Your name is Dana* and points at `@{name}`: dangling, because that slot was never written. `copula-name.tg` declares *You are Aria* and points at `@{you.name}`: also clean, and that one is the interesting result.

Three of the four are what you would guess. The fourth is not, and the only place it is visible is the artifact.

tg build - the same pointer, two declarations

```
$ npx typeglish build you-name.tg
✓ built .typeglish/dist/you-name.txt ← you-name.tg (d006f518b309, full)

# Role
You are a customer support agent for Northbeam Mobile. Your name is Dana.

# Constraints
- You MUST greet a caller as Dana in your first reply.

$ npx typeglish build copula-name.tg
✓ built .typeglish/dist/copula-name.txt ← copula-name.tg (da16c0a7eac4, full)

# Role
You are a customer support agent for Northbeam Mobile. You are Aria.

# Constraints
- You MUST greet a caller as {you.name} in your first reply.
```

The second artifact reaches the model with `{you.name}` in it, from a file that is 0 error, 0 warning, 0 info under `check` and unchanged under `check --strict`. *You are Aria* never wrote the slot, so the pointer has no compile-time target and lowers to a runtime hole for a host to fill. Nothing filled it.

That is worth separating from the failure it resembles. The bare `@{name}` errors because no such symbol exists anywhere. `@{you.name}` does not error because *you* exists and `.name` is a property a host could reasonably supply at request time, which is the same benefit of the doubt every [unbound value on the compile-time plane](https://typeglish.dev/blog/prompt-compile-time-runtime) gets. The consequence is specific and worth writing down: **a property pointer that resolves is proof the slot was written, and a property pointer that ships is proof it was not.**

## §4 The other owner in the file

Halfway down the same prompt there is a second set of possessives, and they are the ones that decide what a customer is offered.

tg check cust/ - three files about the person on the other end

```
cust/balance.tg:5:1  error  logic/contradiction  Conflicts with line 5. Contradiction — single-valued "customer.account.balance" is assigned 2 different values ("40 pounds", "90 pounds"). Keep one, or hold several with a list ("… is 40 pounds and 90 pounds").
cust/balance.tg:5:1  error  logic/measure  Money conflict on customer — "exactly GBP 40" and "exactly GBP 90" cannot both hold. (with line 5)
cust/balance.tg:5:1  error  logic/measure  Money conflict on customer.account.balance — "exactly GBP 40" and "exactly GBP 90" cannot both hold. (with line 5)
cust/balance.tg:6:1  error  logic/contradiction  Conflicts with line 4. Contradiction — single-valued "customer.account.balance" is assigned 2 different values ("40 pounds", "90 pounds"). Keep one, or hold several with a list ("… is 40 pounds and 90 pounds").
cust/balance.tg:6:1  error  logic/measure  Money conflict on customer — "exactly GBP 40" and "exactly GBP 90" cannot both hold. (with line 4)
cust/balance.tg:6:1  error  logic/measure  Money conflict on customer.account.balance — "exactly GBP 40" and "exactly GBP 90" cannot both hold. (with line 4)
cust/tier.tg:5:1  error  logic/contradiction  Conflicts with line 5. Contradiction — "customer.tier" is assigned "gold" and "silver" — both name a metal, and a single-valued slot holds one.
cust/tier.tg:6:1  error  logic/contradiction  Conflicts with line 4. Contradiction — "customer.tier" is assigned "gold" and "silver" — both name a metal, and a single-valued slot holds one.

✗ 3 files — 8 error, 0 warning, 0 info
program: 3 independent files — no $IMPORT compositions
```

`tier.tg` is *The customer's tier is gold* beside *The customer's tier is silver*. `balance.tg` is the chained possessive, reported twice over at two depths. `caller.tg` is *The customer's tier is gold* beside *The caller's tier is silver* and does not appear here at all.

Three things fall out of that report.

- **The proof reaches the values, not just the slot.* gold* and *silver* are not two spellings of a symbol the checker is tracking; the message says *both name a metal, and a single-valued slot holds one*. It knows they are two members of one coordinate set and refuses to let a slot hold both.

- **A chain is a slot at every depth.* The customer's account's balance* reports as `customer.account.balance`, and the money conflict lands twice, once on the full path and once on `customer`. That is the [possessive chain shipped in 0.2.3](https://typeglish.dev/blog/typeglish-0-2-3) doing exactly what it says: each link is addressable.

- **Renaming the owner switches the checker off.* The customer's tier* and *The caller's tier* are the same human being on the same contact, and they are two owners, so `caller.tg` is 0 error, 0 warning, 0 info. If your prompt calls them *the customer* in one section and *the caller* in another, which most contact-centre prompts do, half your facts about that person are unreachable from the other half.

There is a bigger problem with that whole section, though, and it is not the vocabulary. *The customer's tier is gold* is a declaration: it asserts, for every conversation this prompt is ever used in, that the customer is gold. It is not wrong because it conflicts with the line under it. It is wrong because it is a per-conversation fact written as a permanent one, which is [a premise the model reasons from](https://typeglish.dev/blog/agent-certainty-claims) rather than something it can check.

## §5 Bind it once, and let the host say the rest

So the rewrite splits the file's nouns by who owns them and how long the value lasts. The agent's name is fixed for every conversation, so it is a declaration, bound once, referenced by pointer. The customer's tier changes on every contact, so it is an input the host supplies and the compiler resolves.

named.tg - one owner per property ✓ A (99/100)

```typeglish
<$CONFIG>
  $REQUIRE variable customer_tier: one of gold, silver
</$CONFIG>

# Role
@@ identity: one owner, one property, so the next rebrand is a one-line diff
Your name is Dana.
@@ role: chat support for a mobile network
You are a customer support agent for Northbeam Mobile.

# Constraints
@@ greeting: the pointer proves the name was bound, so it cannot reach the model as a hole
- You MUST greet a caller as @{you.name} in your first reply.
@@ brevity: four sentences keeps a chat reply scannable
- You MUST keep every reply to at most 4 sentences.
@@ tier_callback: entitlement is a fact about this caller, so the host supplies it and the compiler keeps one arm
$IF @{customer_tier} is equal to gold:
  @@ gold: the callback is the paid-for part of the tier
  You MUST offer a callback within 15 minutes.
$ELSE:
  @@ other: no tier below gold has a callback to promise
  You MUST NOT offer a callback.

$TEST first_reply
  - input:: Hi, my broadband is down again.
  - expect::
    - contains "Dana"
```

0 error, 0 warning, 0 info at A (99/100), runtime 98 and hygiene 100, with `enforceability 93` named as the remaining lever. The rebrand that started this post is now one line, and the pointer means the greeting rule follows it.

Then the builds are the part you can hand to somebody. Three of them, because the tier has two members and no binding at all is also a state your pipeline can be in.

tg build named.tg --vars - three bindings

```
$ npx typeglish build named.tg --vars '{"customer_tier":"gold"}'
✓ built .typeglish/dist/named.txt ← named.tg (2f21be9f0006, full)

# Role
Your name is Dana. You are a customer support agent for Northbeam Mobile.

# Constraints
- You MUST greet a caller as Dana in your first reply.
- You MUST keep every reply to at most 4 sentences.
You MUST offer a callback within 15 minutes.

$ npx typeglish build named.tg --vars '{"customer_tier":"silver"}'
✓ built .typeglish/dist/named.txt ← named.tg (9f5e8afc6ee2, full)

# Role
Your name is Dana. You are a customer support agent for Northbeam Mobile.

# Constraints
- You MUST greet a caller as Dana in your first reply.
- You MUST keep every reply to at most 4 sentences.
You MUST NOT offer a callback.

$ npx typeglish build named.tg
✓ built .typeglish/dist/named.txt ← named.tg (7b0fc3d95eb4, full)

# Role
Your name is Dana. You are a customer support agent for Northbeam Mobile.

# Constraints
- You MUST greet a caller as Dana in your first reply.
- You MUST keep every reply to at most 4 sentences.
If customer_tier is gold:
  You MUST offer a callback within 15 minutes.
Otherwise:
  You MUST NOT offer a callback.
```

One name in all three artifacts, spelled out, with no pointer left in the text. Two hashes for the two bindings, and a third for the unbound template, which ships both callback rules as prose and is the state to fail your deploy on rather than the state to ship.

tg test named.tg --dry - output

```
$ npx typeglish test named.tg --dry
✓ named.tg  coverage: 0/4 rules exercised
  · first_reply — "Hi, my broadband is down again." (not run)
      ✓ contains "Dana"
✓ 1 prompt — 0 failed
```

One assert, settled offline: the first reply has to contain the name. `coverage: 0/4` is honest and not a mistake, because a case is attributed to the rules it exercises and a greeting case exercises none of the four as written. It is still the one test worth having, because it is the only thing in the toolchain that reads the name the way a customer does.

## §6 Common questions

**Why does my AI agent introduce itself with the wrong name?**

Because more than one line in the prompt claims the name and nothing compared them. A possessive binds a property of its owner, so *Your name is Dana* writes to `you.name`, while *You are Aria*, *Name IS Aria* and *The assistant's name is Aria* each land somewhere else. Six files identical except for that second line separate cleanly: only a second *Your name is* collides, at 2 blocking `logic/contradiction` errors reading `single-valued "you.name" is assigned 2 different values`. The other five check at 0 error, 0 warning, 0 info, and the build ships every name in the file to the model in one document.

**Does TypeGlish catch a system prompt that gives the agent two names?**

Only when both lines are possessives on the same owner. *Your name is Dana* beside *Your name is Aria* is 2 blocking `logic/contradiction` errors and the file is capped at F (50/100). *Your name is Dana* beside *You are Aria*, beside *Name IS Aria*, or beside *The assistant's name is Aria* is 0 error, 0 warning, 0 info at B (84/100) with `consistency 100`, because a different owner is a different address and two claims at two addresses are not a conflict. The same rule runs on the customer: *The customer's tier is gold* beside *The customer's tier is silver* is caught, and beside *The caller's tier is silver* it is silent.

**Should the agent's name be a declaration or a runtime variable?**

A declaration, because the name is the same in every conversation. Bind it once with *Your name is Dana* and reference it with `@{you.name}`, and the pointer compiles the value into the rule so the model reads a name rather than a placeholder. Facts that change per conversation go the other way: a tier, a plan or a balance written as *The customer's tier is gold* is a claim about every caller, so declare it `$REQUIRE variable customer_tier: one of gold, silver` and let the host supply it. The compiler then keeps one arm and the two builds carry one rule each.

**Why is @{you.name} showing up in my deployed prompt?**

Because nothing bound it at compile time. After *Your name is Dana* the pointer inlines the value and the artifact reads *greet a caller as Dana*. After *You are Aria* the same pointer checks at 0 error, 0 warning, 0 info under both `check` and `check --strict`, and the artifact carries the literal text `{you.name}` for a host to fill, because a property pointer with no compile-time target lowers to a runtime hole. The bare `@{name}` does error, at a blocking `structure/undefined-ref`, which is why the pointer is the fastest way to find out what a line actually bound.

Field note

The habit this leaves you with is small and it costs nothing. When you write a fact about somebody into a prompt, write the owner down the same way every time, and then point at it once from a rule. The pointer is the whole trick: it is a one-character question that the compiler has to answer, and the answer tells you whether the rest of the file agrees with you about who owns what. Prompts drift on nouns long before they drift on rules, because a rule gets reviewed and a noun gets copied. Two names is the version of this you can laugh about in a standup. *The customer's tier* and *the caller's tier*, four sections apart, deciding different things about the same person, is the version that reaches a bill.

∿ washed up Aug 21, 2026 ∿
