Digital Twin — A Partner With Standing
What the Digital Twin is
The Digital Twin is not a separate agent and not a persona file. It is the same AI Partner engine, pointed at you as a partner with standing rather than at a task queue. It adds a layer of mechanisms that carry a relationship on top of ordinary chat and goal execution:
| Mechanism | What it gives the twin | File / surface that drives it |
|---|---|---|
| Charter | Standing to refuse, to confront, and to earn autonomy | CHARTER.md |
| Constitutional memory | Founding commitments and corrections that are never summarized away | memory class constitutional |
| Positions + dissent gate | Evidence-backed claims about you it will push back from | memory class positions |
| Confrontation loop | A scheduled, evidence-gated challenge when your activity drifts from your goals | GOALS.md + CHARTER.md |
| Earned delegation | The right to act unasked — graduated per action class by clean approvals | CHARTER.md + approvals ledger |
The founding principle is deliberate: the twin has no name and no "voice." A name would make it a costume. It is invoked by using AI Partner normally — the twin behaviors are ambient. See How you invoke it.
The workspace files, and who reads them
Every user's identity and relationship live in plain Markdown files in their own workspace. On a single-user install that is the base workspace; on a multi-user VPS each user has their own copy (see Multi-user on a VPS).
| File | Belongs to | Read by | Purpose |
|---|---|---|---|
SOUL.md | The agent | Every chat + goal turn | The agent's identity, character, work standards. Not your personal facts. |
USER.md | You | Every chat + goal turn (as [USER CONTEXT]) | The single source of truth for who you are — your name, role, stack, preferences. |
CHARTER.md | The partnership | Before every confrontation + delegated action (hot-reloads) | Confrontation domains, resurface policy, delegated authority, founding episode. |
GOALS.md | You | Weekly goal review + twin confrontation | Your active/paused long-horizon goals ("arcs"). |
HEARTBEAT.md | The agent | Every heartbeat tick | Standing proactive tasks (briefings, scans). Distinct from the twin's charter-driven schedule. |
AUTHORITY.md | You | Every action the agent takes for you | Allow / draft-and-ask / block rules; always wins over earned delegation (instant kill switch). |
Your name lives in USER.md, not SOUL.md. USER.md is injected into every prompt as [USER CONTEXT]. If you edit your name and the agent still uses the old one, you almost certainly edited the wrong workspace's copy — see Troubleshooting.
Enabling and configuring
The twin is on by default. Two config flags govern it (in ~/.mindful-assistant/config.json, or per-instance on the server):
| Flag | Default | Effect |
|---|---|---|
twinConnectEnabled | on | Master switch. false disables all twin behavior, including the relationship sections injected into prompts. |
twinDelegationEnabled | on | Org-wide delegation kill switch (exposed to admins). false blocks all earned autonomy while leaving memory/confrontation intact. |
To use the twin to its full potential you configure CHARTER.md — it hot-reloads on save, no restart:
## Granted Confrontation Domains
# The twin may challenge you ONLY in these domains, and only with >= `evidence`
# distinct observations, at most once per week overall. Empty = never confronts.
- domain: stated_goals_vs_activity + evidence: 3
- domain: project_build_arcs + evidence: 3
- domain: health_habits + evidence: 3
## Resurface Policy
- suppression_cooloff_days: 14
- max_suppressions_per_arc: 3
- third_suppression: present_all_three # third back-off shows all three, then retires the arc
## Delegated Authority
# Action classes the twin may EARN after `graduate_after` clean approvals.
# apply: absent_only = used only when you're unreachable. Not listed = never eligible.
- action_class: send_telegram_dm + graduate_after: 5 + apply: absent_only
- action_class: reply_email + graduate_after: 5 + apply: absent_only
## Founding Episode
<!-- Paste your founding-conversation document here. Ingested once as the twin's
first constitutional memory. Leave empty to skip. -->
Removing a domain removes the twin's standing in it immediately. An empty Granted Confrontation Domains section means the twin never confronts you at all. Deny-by-default in both directions: a domain not listed grants no standing; an action class not listed is never delegable.
To actually exercise the full loop you also need:
- A connected messaging channel — Telegram, Discord, or Slack (Settings → Messaging). This is how confrontations and daily digests reach you.
- A populated
GOALS.md— the source of the "arcs" the twin holds you to.
How you invoke it
You don't summon the twin — you just talk to AI Partner. When twinConnectEnabled is on, its relationship memory, open threads, and held positions are injected into every system prompt, so the agent will disagree with you, cite dated evidence, and hold questions open across sessions on its own.
The places you deliberately engage the twin layer:
- 1Chat normally
Speak to AI Partner as usual. It records corrections, forms positions about you, and opens/resolves threads via its
twin_recordtool as the conversation unfolds — you never call the tool yourself. - 2Amend the charter by intent
Say "I want to amend the charter" in chat. The twin restates the current clause, the proposed clause, and the consequence, then asks for explicit confirmation before editing
CHARTER.md. (Direct edits to the file also work and hot-reload — the ceremony just makes constitutional changes deliberate.) - 3Answer confrontations
When your activity drifts from your goals, the twin delivers one charter-gated challenge over your messaging channel (and in-app) with
engage / adjust / back_offbuttons. A "back off" is obeyed and remembered; silence or timeout is never treated as backing off. - 4Use the "Your Twin" panel
Open the Proactive view → Your Twin. From there you can answer pending asks in-app, resolve open threads, revoke an earned delegation instantly, and read the twin's positions, action feed, and charter.
Multi-user on a VPS
On a multi-user instance every user is an isolated tenant. This is the setup you use when hosting AI Partner on a VPS for a team.
Per-user isolation
Each user created in the Admin Console gets their own workspace, seeded with the full template set (including CHARTER.md, USER.md, SOUL.md, GOALS.md):
<workspace_dir>/ # base — the 'default' / single-user workspace
<workspace_dir>/<userId>/ # tenant workspace for each real user
├── SOUL.md ├── USER.md
├── CHARTER.md ├── GOALS.md
├── AUTHORITY.md └── HEARTBEAT.md
Everything the twin knows is scoped to the login:
- Chat and goal prompts read that user's own
SOUL.md/USER.md— so a user editing theirUSER.mdchanges their agent, and no one else's. - Confrontations and digests are delivered on that user's own connected bot (per-owner bot resolution), never crossed between tenants.
- Constitutional memory, positions, open threads, suppressions, and earned delegations are all keyed by
user_id.
Admin governance — conduct, never content
Admins govern twins without ever seeing their content. The Admin Console exposes:
| Control | What it does | What it never exposes |
|---|---|---|
| Twins list | Which users have an active twin, and delegation status | — |
| Earned-action feed | What each twin did under delegation, with receipts | — |
| Per-user freeze | Suspend a twin's delegated autonomy | — |
twinDelegationEnabled | Org-wide delegation kill switch | — |
An admin may freeze; only the user can re-earn. Admins never see a twin's memories, positions, confrontation text, back-off reasons, or goals. Actions taken in the organization's name are org business; the relationship is the user's.
Get the most out of it — an owner's walkthrough
Do these once and the twin moves from "on" to "actually a partner":
- 1Make USER.md real
Put your actual name, role, and working style in your
USER.md. This is the single source of truth for who you are and is read on every turn. - 2Grant confrontation standing
In
CHARTER.md, list the domains you want to be challenged on (e.g.stated_goals_vs_activity). Start with anevidenceof 3 so the twin only speaks when it has real grounds. - 3Give it goals to hold you to
Populate
GOALS.mdwith your genuine active goals. The twin compares these against your actual weekly activity — no goals, nothing to confront. - 4Connect a channel
Connect Telegram (Settings → Messaging). This is where the weekly confrontation and the daily "while you were away" digest arrive.
- 5Let it earn delegation
List a low-risk action class in
CHARTER.mdDelegated Authority (e.g.send_telegram_dm,absent_only). Approve its drafts cleanly a few times; aftergraduate_afterclean approvals it graduates to acting unasked — and one edit or skip revokes it instantly. - 6Correct it, and watch it stick
When it's wrong about you, say so. Corrections become permanent constitutional memory — a corrected mistake never repeats — and revise the underlying position out loud, with history.
- 7Optional: write a founding episode
Paste the conversation that defines the partnership into the
## Founding Episodesection ofCHARTER.md. It is ingested once as the twin's first constitutional memory — the record of how, and why, this partnership began.
Troubleshooting: "my name isn't picked up"
The name the agent uses comes from USER.md (injected as [USER CONTEXT]). Everything below is fixable from inside the app — no server access needed. If a name change doesn't take:
- Edit
USER.mdin the in-app Workspace browser. Open Workspace in the app and editUSER.mdthere — that editor always points at your own workspace, on any install. Changes apply from the next message. (Self-hosting admins editing files on the server disk: each user's copy lives at<workspace_dir>/<userId>/USER.md, not the base workspace — but the in-app editor is the safe path.) - A stale name in
SOUL.md. Workspaces set up on older versions may still carry your name insideSOUL.md(the agent's identity file). OpenSOUL.mdin the same Workspace browser and remove any## User/ "You work primarily with…" line soUSER.mdis the single source. - Memory recall. If you told the agent a different name in earlier conversations, it may recall it. Correct it in chat ("my name is X, not Y") and the correction becomes permanent.
Rule of thumb: SOUL.md = the agent. USER.md = you. One fact, one home.