Skip to Content
EngineeringDecisions (ADRs)ADR 0021 Persona twins: bound agent configurations that propose, never decide

ADR 0021: Persona twins: bound agent configurations that propose, never decide

Status
Accepted
Date
Deciders
Founder

Context

Phase 2 gave every person a console to talk to agents. Work still starts with a person opening a thread; proactive agent work exists only for SOP flows. The founder asked that every persona have a digital twin that does the plumbing and asks its person for approvals, with the aim that stages complete in less elapsed time (a claim that waits for measured data, decision 0025) and with every decision still human and recorded.

Decision

  • A persona twin is a twin_definitions row binding (program_id, user_id, persona_key) to standing duties, triggers, a schedule and a tool allowlist computed as the intersection of the role catalogue and the person’s live permissions. It runs the existing console orchestrator and specialists under run tokens acting for the bound person (requested_by = user_id, act = user_id). It is never a user, signer, approver or sender: the catalogue’s forbidden tool verbs make those structurally absent.
  • Duties run under a standing plan approved once by the bound person (permission_exercised = program.twins.use), hashed and re-asked on any change of tools, permissions or agent version. Reads run unattended (fetch ingestion capped per cycle and disclosed); every record write is a gated proposal.
  • A twin cycle files and finishes: proposals are tool_calls rows behind detached interrupts; the run ends succeeded; approval later executes the write server-side under the resolver’s principal. Interrupts on a twin run belong to the bound person (write approvals also to an active delegate holding the write’s own permission); everyone else gets 403 bound_user_only.
  • Persona version 3 subtracts evidence.hypotheses.approve and plans.plans.approve from the scientist so the approval matrix and the code agree; the matrix is generated from BUILTIN_ROLES by test. Keys program.twins.read, program.twins.read_own, program.twins.use, program.twins.manage, program.handoffs.read and program.handoffs.manage ship in R3 (81 -> 87); org.twins.use ships only with R5.
  • One active twin per (program_id, user_id); viewers have none; removal from the program, deactivation or lost permissions pause the twin in the same transaction.

Consequences

  • Run kinds twin_cycle, twin_handoff; interrupt kind batch_approval (at most 10 items, explicit selection, per-item permission and audit, partial decisions execute immediately); tool_calls.basis_hash refuses stale proposals (409 stale_proposal).
  • Notification fatigue is bounded by digest filings (notify=False), quiet hours that hold delivery not the record, fixed short “now” lists, backpressure above 20 open items, self-pause on budget or high decline rate.
  • Phase-2 threads.schedule rows migrate to paused twins with a notice; nothing runs until the person approves a standing plan.

Alternatives considered

  • Twins with their own persona and permissions: an agent that can approve or sign is exactly what the platform forbids; rejected.
  • Chat between twins: hidden coordination; rejected in favour of hand-off records (decision 0023).
  • One shared program agent: loses the per-person binding that makes approvals meaningful; rejected.

Follow-ups

  • Cost budget in money once the runtime reports usage.cost_usd (R3 contract test).
  • Organisation twins (R5).
Last updated on