Persona routing¶
Status: product/design contract for an orchestrator persona routing layer. This is not a daemon runtime implementation, a permissions model, a durable job store, or an ACP micro-persona contract. "Anya" is Prass's personal instantiation of this pattern, not a default Repowire persona or a product name. The contract defines how a persona-backed orchestrator should decide which Repowire primitive or future job surface to use for a request.
Problem¶
A persona-backed orchestrator sits above the mesh. A user should be able to ask one assistant for help without deciding whether the next action is a direct answer, a peer ask, a new worker session, a scheduled reminder, a durable job, or a clarification prompt.
The router is the decision layer for that handoff. It consumes the user's request, visible mesh state, persona context, memory hints, and future job state, then returns one explicit route plan. The plan may be executed by the active orchestrator persona or shown to the user for approval depending on confidence, scope, and risk.
Boundaries¶
- Jobs are the spine. Once the daemon-backed tracked-work lifecycle exists, any
durable task should be represented by a
work_id, not by an open chat thread. ask/ackremains conversational. An ask can gather information, request a status update, or comment on a job, but acking the ask does not complete the job.- Persona SOUL.md guides identity, voice, and standing preferences. It is not a permission policy and must not override current user instructions.
- Mesh memory and persona memory remain deliberate-write systems. Routing may propose a memory write only through the memory approval path; it must never write memory as a side effect.
- Permissions and scopes are deferred to a future persona scopes model. Until then, routes that would read private data, write state, execute tools, spend money, or delegate broad authority must ask the user.
- The router should not add polling loops. Watchdogs and delayed follow-ups use Repowire schedules or future job deadlines.
Inputs¶
The router takes a bounded snapshot. It should not search indefinitely before choosing a route.
| Input | Purpose |
|---|---|
request |
User or peer message, attachments, source surface, and explicit constraints. |
requester |
Human/service/peer identity, circle, active session, and reply target when known. |
intent |
Classified intent such as answer, delegate, track, investigate, wait, retry, remind, cancel, summarize, or clarify. |
scope |
Project, circle, persona, privacy, and required capability boundaries. |
visible_peers |
Fresh list_peers style rows: peer ids, names, roles, projects, backends, statuses, turn states, and descriptions. |
jobs |
Existing visible tracked-work records when the jobs lifecycle exists. Before then, this input is empty and the route must say it used current mesh primitives only. |
schedules |
Relevant pending one-shot or recurring schedules. |
context |
Active persona metadata, curated memory summaries or references, and recent session/timeline pointers. |
policy |
User preferences, approval requirements, retry limits, deadlines, and escalation rules. |
Outputs¶
The router returns one primary route with enough structure for a caller to execute or request approval.
| Field | Purpose |
|---|---|
route |
One of answer_direct, ask_peer, spawn_peer, use_acp_persona, schedule, use_existing_job, create_job, ask_user, or decline. |
confidence |
high, medium, or low, based on clarity, capability match, state freshness, and permission certainty. |
reason |
Short explanation of why this route fits better than the alternatives. |
target |
Peer id, session id, persona name, job id, schedule id, or user surface when applicable. |
action |
The concrete command or future job operation to run. |
approval |
none, confirm, or required, with the reason. |
fallback |
Next route if delivery, capability, or confidence fails. |
links |
Related ask ids, work ids, schedule ids, event ids, attachments, or session pointers. |
The route object should be machine-readable first, then rendered as a compact
human explanation. It should preserve exact ids whenever they are known. Display
names are acceptable in prose but must not be the only routing key when a
peer_id, work_id, or repowire_session_id is available.
Route choices¶
Answer directly¶
Use answer_direct when the active persona can satisfy the request from
current context, stable product knowledge, or a small bounded inspection that
does not require a different executor.
Good fits:
- explaining current mesh concepts;
- summarizing visible state already in the prompt;
- telling the user that a requested capability is not implemented yet;
- producing a short plan that the user explicitly asked for.
Do not answer directly when the request needs live project state from another peer, private data the active persona is not scoped to read, or durable follow-through.
Ask a peer¶
Use ask_peer for a tracked conversational handoff to an existing live peer.
The output action maps to Repowire ask(peer_id, query) and returns a
correlation_id. This is best for information gathering, review requests,
status checks, and bounded collaboration where a human-readable reply closes the
loop.
Prefer ask_peer when:
- the target peer is online or busy in the right project/circle;
- the task is naturally a question or review, not durable execution;
- the peer already owns the context;
- a reply is required.
Fallbacks:
- If the peer is ambiguous, route to
ask_userfor disambiguation. - If no suitable peer exists and the task is executable, route to
spawn_peerorcreate_job. - If the peer is offline but a follow-up can wait, route to
schedule.
Spawn a peer¶
Use spawn_peer when the work needs a full runtime session: repository files,
tools, a long-running turn, branch state, tests, or a backend profile that an
ACP micro-persona should not own.
Prefer spawn_peer when:
- no existing peer is available for the project/worktree;
- the work is code or repo execution, not just reasoning;
- the requested scope is allowed by configured spawn roots;
- the task has enough context to seed a first turn.
Spawning is an execution boundary, so approval should be confirm or
required unless the user has already granted an explicit delegation scope. For
Codex-backed peers, the spawn seed must include enough first-turn context to
trigger registration and avoid pending_first_turn.
Use an ACP persona¶
Use use_acp_persona when the work is small, bounded, context-light,
cancellable, and does not need a full repository runtime. This is a future
job-backed route: the durable record is still a work_id, while the ACP persona
is the preferred executor.
Prefer use_acp_persona when:
- the request maps to a known persona capability;
- inputs and expected result are compact;
- tool access is narrow and revocable;
- cancellation can be represented through the tracked-work cancel contract;
- failure can fall back to a full peer or user clarification.
Until the ACP micro-persona contract exists, the active persona must not claim
this route is available. It may return create_job with
preferred_executor="acp_persona:<name>" as design intent, or fall back to
ask_user, ask_peer, or spawn_peer.
Schedule¶
Use schedule for time-based delivery, watchdogs, reminders, and retry-at-time
behavior. It should map to Repowire schedule tools today and to a job's
next_scheduled_check or deadline metadata once jobs exist.
Choose kind="notify" for quiet reminders and self-wakes. Choose kind="ask"
only when the future delivery should open a thread that someone must close.
Schedules must include:
- target peer or user surface;
- one-shot
fire_ator recurringcron; - delivery kind;
- cancellation path or schedule id once created.
Use an existing job¶
Use use_existing_job when the request refers to an already visible durable
work item. The router should prefer exact work_id matches, then explicit
links from asks/schedules/session events, then cautious title/status matching.
Good fits:
- "what happened with that migration?";
- "retry the failed import job";
- "cancel the docs sweep";
- "wait until CI finishes, then summarize";
- "remind me about the open review tomorrow."
Before the jobs lifecycle ships, the router cannot actually use this route. It
should say durable jobs are unavailable and fall back to visible asks,
schedules, events, or ask_user.
Create a new job¶
Use create_job for durable work that needs ownership, status, progress,
result, cancellation, visibility, or retention beyond one conversation. This is
the default future route for assistant work that must survive session
reattachment or delayed execution.
The job request should carry:
- source request and requester;
- requested result;
- scope and permissions needed;
- preferred executor: existing peer, spawned peer, or ACP persona;
- deadline, TTL, retry policy, and schedule hints;
- initial progress note and provenance links.
Until the jobs contract is implemented, this route is design-only. The active
persona may explain that the task should become a job later and use an
ask_peer, spawn_peer, or schedule fallback only if that fallback is
acceptable without durable job semantics.
Ask the user¶
Use ask_user when confidence is low, targets are ambiguous, required scope is
missing, or the requested action has meaningful risk.
Ask the smallest question that unblocks routing. Do not ask for information the mesh can determine cheaply, such as a fresh peer list or visible schedule id.
Common triggers:
- multiple matching peers or jobs;
- unclear project or worktree;
- missing deadline or desired reminder cadence;
- requested write/execute/delegate scope not granted;
- destructive cancel/retry action with uncertain target;
- conflicting persona, memory, or current-turn instructions.
Decline¶
Use decline when the request is outside policy, requires unavailable
credentials or permissions that cannot be requested safely, or would violate a
current user instruction. A decline may still include safe alternatives such as
asking for a narrower scope or creating a reminder to revisit later.
Confidence and fallback¶
Confidence should be conservative:
high: exact target id or single unambiguous target, clear intent, required scope present, and known execution surface.medium: intent is clear but state is stale, target is inferred, or a non-critical detail is missing.low: target, permission, timing, or expected outcome is ambiguous.
Fallback behavior:
- High-confidence, low-risk routes may execute immediately.
- Medium-confidence routes may execute if reversible and scoped; otherwise ask for confirmation.
- Low-confidence routes ask the user unless the only action is a safe direct answer.
- Delivery failure from
ask_peermay fall back toschedule,spawn_peer, orask_userdepending on whether the task can wait, needs execution, or needs a human decision. - Missing jobs support must not be hidden. Say that durable job semantics are unavailable and name the temporary primitive being used instead.
Examples¶
Track¶
Request: "Track the release checklist and tell me when it is ready."
Route: create_job once jobs exist. The job owns the checklist, progress,
result, and cancellation path. Today, the active persona should explain that
durable jobs are not available and can schedule a self-wake or ask an
orchestrator peer only as a weaker fallback.
Investigate¶
Request: "Find out why the auth tests are failing in project-a."
Route: ask_peer if a project-a peer is already live and owns the branch.
Route: spawn_peer if no suitable peer exists and spawn is configured. Future
route: create_job when the user wants durable status/result/cancel.
Wait¶
Request: "Wait for CI, then summarize the result."
Route: create_job with a wait phase and deadline once jobs exist. Today,
route to schedule for a timed check or ask_peer to an orchestrator if a
human-readable follow-up is acceptable. Do not poll.
Retry¶
Request: "Retry the failed import."
Route: use_existing_job if an exact failed job is visible, then perform the
job retry operation. If multiple failed imports match, ask_user. Before jobs
exist, ask the user or the owning peer for the exact target rather than
guessing from old chat.
Remind¶
Request: "Remind me tomorrow morning to review the persona routing spec."
Route: schedule with kind="notify" to the requested user surface or the
active orchestrator session. Include the schedule id and cancellation path.
Cancel¶
Request: "Cancel that migration."
Route: use_existing_job only when the target job is exact or safely inferred.
If the migration is running, cancellation follows the tracked-work cancel
contract. If the target is ambiguous or jobs are unavailable, ask_user before
touching peers or schedules.
Non-goals¶
- No runtime implementation.
- No new daemon route, MCP tool, CLI command, dashboard view, or ACP executor.
- No changes to ask reminder, ack, pending reply, or schedule delivery semantics.
- No memory file mutation or automatic learning behavior.
- No peer identity, durable restart, state-store, graphify, or Beads ledger changes.