Two things were still missing before the persona was presentable. It had to be fully believable, not half-coherent, and it had to be honest about what it had done. Three decisions covered both.
Correlate fully, or not at all
The first fork was how deep the correlation goes. Name-coherence only (first, last, full, email, username agree) is cheaper. But half-coherence is its own kind of broken: a FirstName of "Maria" next to a Salutation of "Mr.," or an Age that contradicts the BirthDate, looks as wrong as fully random data, and the seed/demo reader is exactly the person who notices.
So the persona is a full bundle. Some facets are drawn, the rest derive with no extra randomness:
drawn: gender, firstName (conditioned on gender), lastName,
birthDate (adult-aged, off the operation's reference time),
phone (locale format), emailDomain
derived: fullName, initials, email, username, salutation (from gender),
age (from birthDate + reference time)
This is not free: it adds a dataset dependency, first names tagged by gender. I accepted that, because the alternative is data that fails the two-second believability test.
Gender, without shipping a gender enum
The persona needs an internal gender axis to pick a coherent name and salutation.
That raised a question I wanted to handle carefully: does Munchausen now ship a public gender enum? I decided no.
The fork was whether to leave a model's own gender field alone (and risk it contradicting the name) or bind it. I bind it, by adapting to whatever type the model already declares:
class Account { string FirstName; string Gender; } // string -> "Female"
class Profile { string FirstName; Sex Sex; } enum Sex { Female, Male, Unspecified }
// enum -> Sex.Female
class Contact { string FirstName; } // no gender field
// axis still drives the name
A string member gets "Female" or "Male". An enum member gets the value whose name matches the internal axis, falling back to ordinary inference when nothing matches. Munchausen never defines the categories itself; it mirrors the type the model declares, and the explicit d.Person.Gender is a plain string for the same
reason. The internal-axis-only option lost because it leaves the gender field free to contradict the name, which is the exact incoherence I was removing, just moved one field over.
Make it inspectable
Explainability is a differentiator on the rigor axis, so coherence cannot be a silent behavior. Each persona-bound member reports a new source, InferenceSource.CoherentEntity, and Explain() shows it plainly:
Contact.FirstName -> Person.First [coherent entity]
Contact.LastName -> Person.Last [coherent entity]
Contact.Email -> Person.Email [coherent entity] (fulfills [EmailAddress])
Contact.Gender -> Person.Gender [coherent entity]
You can see which members share one identity (they all say Person.*), and the compose decision from the last post shows up inline on Email. The same report is what makes the two-people collapse diagnosable.
Takeaway
Correlate fully or not at all, because partial coherence reads as broken. Adapt to the types your users declare instead of legislating your own. And never let an automatic behavior be opaque: inference you can read is inference you can trust.
What's next
A persona engine that only does Person is just a Person feature with extra steps. The real test of the abstraction was a second entity. Next post: proving it generalizes, from Person to Address.
Top comments (0)