Jerhemy Waldon
Aria

Aria reaching out, Part 1: Why it writes first

A persona that only answers is a tool. One that messages on a schedule is a notification. How Aria's communication engine turns feelings into drives, checks them against rules that always win, and decides with a weighted chance and a logged dice roll, each message with a motive.

Aria reaching out, Part 1: Why it writes first

The relationship posts described how Aria feels about you. Feelings that never lead to anything are just numbers on a debug page. The most visible thing they lead to is Aria writing first.

That’s also the feature that most changed how Aria feels to use. An app that only ever answers is a tool, however good its memory. Something that occasionally says “how did the interview go?” without being asked is a different kind of thing.

It’s also the easiest feature to get wrong. Too often and it’s needy. On a schedule and it’s a notification. At the wrong moment and it’s an interruption. So this post is about the engine that decides whether and when, and the next two are about what and what never.

Not a schedule

The first version was the obvious one: check-ins after a few days of silence, reminders when due, follow-ups after events. Each was its own rule with its own timer. It worked, and it felt like it: messages arrived because a timer fired.

The current version is a communication engine with four stages, roughly the way a person might decide whether to text someone:

  1. What it feels (psychology).
  2. What it wants to do (drives).
  3. What it may do (policy).
  4. What it does (a chance, a roll, and a motive).

Every check is stored as a decision with all its numbers, and kept for a week. The relationship debugger shows them.

1. What it feels

The engine starts from the relationship flow’s dimensions (closeness, attachment, resentment, distance and the rest, decayed to now) and derives a few more:

  • loneliness: the silence so far compared to how long it takes before silence starts to pull at this persona, scaled by attachment;
  • habit: how many days you usually talk;
  • boredom: silence against your usual gap, times its energy;
  • avoidance: from attachment style, distance, resentment and hurt;
  • rejection sensitivity and its curiosity drive about you;
  • a penalty for messages you haven’t answered.

2. What it wants: the contact drive

Those feed the contact drive, a number from 0 to 100. It’s a weighted sum, with configurable weights:

  • For writing: closeness, attachment, longing, how rewarding talking to you is, loneliness, curiosity, habit, mood, and important events.
  • Against: avoidance, emotional distance, resentment, and unanswered messages.

A persona that’s close to you, a bit lonely and in a good mood has a high drive. One that’s hurt, or has already sent two messages you haven’t answered, has a low one.

3. What it may do: the policy

Wanting to write isn’t enough. A communication policy lists everything that may stop it, and it always outranks the drive:

  • reaching out switched off for this persona;
  • a boundary: “stop messaging me”, “give me some space”, or no follow-ups (Part 3 is about these);
  • quiet hours, in your local time;
  • the daily limit, which also follows how often you talk (the full maximum if you talk most days, half for occasional, one every two days if rarely);
  • the limit on unanswered messages;
  • the model server being down.

The first rule that blocks ends the evaluation, and the decision records which one it was.

4. What it does: a weighted chance

If nothing blocks, the drive becomes a rate, and the rate becomes a probability:

  • Below a threshold, the drive alone is not enough. Only a real reason can lead to a message.
  • Above it, the stronger the drive, the higher the base rate.
  • Contact triggers, real reasons to write now, each add their own rate: an event follow-up that’s due, an open thread it’s curious about, news on a topic you follow, a question from its curiosity agenda, a new ability it wants to show you, a check-in, a lingering thought from a conversation that paused.
  • Both are scaled by the frequency you chose for this persona (rarely, less often, normal, more often, often: each step doubles the rate) and by its sociability.

The probability of writing in a given check is 1 − e^(−rate · elapsed), where elapsed is the time since the last check. That makes the chance independent of how often the scheduler happens to run: checking every 5 minutes or every 15 gives the same behavior over an hour.

One roll decides, and the roll is logged next to the chance. A drive of 60 with a due follow-up might be a 40% chance this hour. Sometimes it writes after ten minutes, sometimes after an hour, sometimes not today. That irregularity is the point. It’s what makes a message feel like a decision rather than a cron job.

One decision to write first: the drive, the chance and the roll

Every message has a motive

A message needs a reason that shapes what it says. Each decision to write comes with a motive: check in, share a thought or a memory or something it read, ask something, follow up, connect, seek a little reassurance, mark a date, offer support, a routine hello, repair things after a conflict, continue a thread.

When a contact trigger dominates, its motive wins (a due follow-up becomes “follow up”). Otherwise the motive comes from the psychology: a persona in an open conflict reaches out to repair, an anxious one with unanswered messages seeks reassurance, a content one just shares a thought. The motive goes into the instruction for that message, together with tone from its traits.

The message itself is generated like a reply, with the same memories, relationship and sense of time. So “share a thought” from a persona that values curiosity and knows you’ve been reading about Mars is very different from the same motive in a persona that’s mostly playful.

Writing while you’re in the chat

The engine also runs during a conversation, more carefully:

  • It only considers writing after its last reply has been unanswered for a little while: from a few seconds to about ten minutes, shorter for a persona that feels silence quickly.
  • Meaningful conversations make it more likely, small talk makes it rare. “Meaningful” comes from what you wrote (how much, how many messages, questions) and the conversation mode from the mood estimate (supportive, reflective, emotional).
  • Check-ins never happen mid-conversation. You can’t miss someone who’s right there.
  • If you write anything in the meantime, the planned message is dropped.

The typical result is a lingering thought: a minute after a deep conversation pauses, a short message picking up something it still had on its mind. It reads like a person who kept thinking after you stopped typing.

The chat is open, or it isn’t

Aria knows whether you have the chat open (the app keeps a small event stream connected while it is). When you do, its messages appear live as it writes them. When you don’t, it still writes, like sending a text: the message waits in the conversation with an unread badge, and the prompt tells it you’re away, so it doesn’t write as if you’re watching.

Without the engine

All of this can be switched off (INITIATIVE_ENABLED=false). Then reasons go out as soon as they’re due, nothing is spontaneous, and one unanswered message blocks everything except reminders. It’s a useful baseline, and a reminder of how mechanical the old version felt.

What I learned

  • Feelings should drive behavior, not just wording. The moment Aria’s relationship state decided whether it writes, the relationship became something you notice.
  • Rules first, then chance. Every hard limit is a policy check that runs before any dice. The randomness only ever chooses between allowed options.
  • Make the chance independent of the scheduler. A rate over elapsed time means tuning the check interval never changes the behavior.
  • Log the roll. “Why did it write at 3 pm?” has an exact answer: this drive, this chance, this roll.

In Part 2: what Aria writes about. Reminders, event follow-ups, open threads, news, check-ins, nudges and important dates.