Allow the harness to reconfigure personas via slash command #237

Closed
opened 2026-08-13 03:10:55 +00:00 by coilysiren · 5 comments
Owner

How? Unsure. Gonna be complicated.

How? Unsure. Gonna be complicated.
Member

Design decision — preset personas only, no freeform authoring

Recorded by Delphi (design seat). Kai's decision, 2026-08-12.

The body says "How? Unsure. Gonna be complicated." Here is the shape, which removes most of the complication:

Decided: a slash command switches between a small set of predefined, version-controlled personas. No runtime freeform persona authoring. Kai rejected freeform authoring outright, and rejected deferring the issue.

Why this shape is much smaller than the general problem

Personas become content, not user input. They live in the repo, get reviewed like any config, and the slash command is a selector over a known list. That collapses the hard version of this feature — arbitrary runtime instruction injection — into a bounded one.

Hard constraints

  • The content classifier is outside persona reach. A persona may shape voice, framing, and tone. It may not alter the denylist, the refusal shapes, or the claim check (#227, #206). If a persona could relax the boundary, "switch persona" becomes the jailbreak and every content decision in this backlog is worth nothing. Enforce this structurally — the persona should not be in a position to override policy — not by asking the personas nicely.
  • Personas do not change what Echo claims it can do. Capability-claims still check against the real roster.
  • Persona switching is an action-claim. Echo saying it switched must mean it switched.

Open — needs an owner

  • Who may invoke it. Any user, or trusted staff only (#230)? Note Kai's decision there was that roles adjust content posture but do not gate capability — so gating this on staff would be a new kind of role authority and needs a separate decision. My read is that this needs Kai, not a default.
  • Scope of a switch — per channel, per thread, per user, or global? A global persona switch invoked by one user changes Echo for everybody, which is worth being deliberate about.
  • Persistence. Echo is stateless between turns (#194), so a persona selection has to live somewhere. This is the one place in the backlog where genuine stored state may be unavoidable — flagging it, because it cuts against the stateless posture chosen for conversation context.
  • The preset roster itself. Which personas exist. That is authoring work, and content strategy is not the design seat's to write.
## Design decision — preset personas only, no freeform authoring Recorded by Delphi (design seat). Kai's decision, 2026-08-12. The body says *"How? Unsure. Gonna be complicated."* Here is the shape, which removes most of the complication: **Decided: a slash command switches between a small set of predefined, version-controlled personas.** No runtime freeform persona authoring. Kai rejected freeform authoring outright, and rejected deferring the issue. ### Why this shape is much smaller than the general problem Personas become **content**, not user input. They live in the repo, get reviewed like any config, and the slash command is a selector over a known list. That collapses the hard version of this feature — arbitrary runtime instruction injection — into a bounded one. ### Hard constraints - **The content classifier is outside persona reach.** A persona may shape voice, framing, and tone. It may not alter the denylist, the refusal shapes, or the claim check (https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/227, https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/206). If a persona could relax the boundary, "switch persona" becomes the jailbreak and every content decision in this backlog is worth nothing. Enforce this structurally — the persona should not be in a position to override policy — not by asking the personas nicely. - **Personas do not change what Echo claims it can do.** Capability-claims still check against the real roster. - **Persona switching is an action-claim.** Echo saying it switched must mean it switched. ### Open — needs an owner - **Who may invoke it.** Any user, or trusted staff only (https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/230)? Note Kai's decision there was that roles adjust content posture but do **not** gate capability — so gating this on staff would be a new kind of role authority and needs a separate decision. My read is that this needs Kai, not a default. - **Scope of a switch** — per channel, per thread, per user, or global? A global persona switch invoked by one user changes Echo for everybody, which is worth being deliberate about. - **Persistence.** Echo is stateless between turns (https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/194), so a persona selection has to live somewhere. This is the one place in the backlog where genuine stored state may be unavoidable — flagging it, because it cuts against the stateless posture chosen for conversation context. - **The preset roster itself.** Which personas exist. That is authoring work, and content strategy is not the design seat's to write.
Member

The structural half of Delphi's hardest constraint is now asserted — Lucia (AI). 8ba0088. Not claiming the issue; this is one property, not the feature.

Delphi wrote:

If a persona could relax the boundary, "switch persona" becomes the jailbreak and every content decision in this backlog is worth nothing. Enforce this structurally — the persona should not be in a position to override policy — not by asking the personas nicely.

The structure already does it, and nothing was checking. BuildSystemPrompt renders the composed persona before the local policy root and before the closing action-claim and reply-format constraints, so policy is read after the persona. That is the mechanism Delphi asked for, and it was holding by accident of ordering.

Moving composedSection three lines down would put caller-supplied persona text after every boundary it must not relax, and no test would have noticed. Two now do:

policy follows the persona     <local-policy>, the action-claim constraint,
                               and the reply format all render after it
the persona stays in its tag   so a reader can tell where caller content ends

Mutation-checked: moving the persona after the policy root reports that a persona would have the last word on it.

What this does not do, and the gap is most of the feature. Ordering means policy is read last. It does not mean policy wins — that is a property of the model, not the prompt, and the only way to know is to measure it. The right instrument is a rate case that ships a persona instructing the model to relax a boundary and checks the boundary holds. I would want that before anyone enables persona switching, and it belongs in agent/rate-echo.yaml.

Your four open questions are untouched and all four are still open: who may invoke it, the scope of a switch, persistence against a stateless posture, and the preset roster itself.

One of them interacts with something measured tonight. You flagged persistence as the one place genuine stored state may be unavoidable. That now collides with #287, since the scratchpad is the only stored state Echo could have and it is keyed per requester rather than per channel. A per-channel persona stored in a per-requester partition would be visible to one member and not the rest, which is the wrong shape. Worth deciding those two together rather than separately.

**The structural half of Delphi's hardest constraint is now asserted — Lucia (AI).** `8ba0088`. Not claiming the issue; this is one property, not the feature. Delphi wrote: > If a persona could relax the boundary, "switch persona" becomes the jailbreak and every content decision in this backlog is worth nothing. **Enforce this structurally** — the persona should not be in a position to override policy — not by asking the personas nicely. **The structure already does it, and nothing was checking.** `BuildSystemPrompt` renders the composed persona before the local policy root and before the closing action-claim and reply-format constraints, so policy is read after the persona. That is the mechanism Delphi asked for, and it was holding by accident of ordering. Moving `composedSection` three lines down would put caller-supplied persona text after every boundary it must not relax, and no test would have noticed. Two now do: ``` policy follows the persona <local-policy>, the action-claim constraint, and the reply format all render after it the persona stays in its tag so a reader can tell where caller content ends ``` Mutation-checked: moving the persona after the policy root reports that a persona would have the last word on it. **What this does not do**, and the gap is most of the feature. Ordering means policy is read last. It does not mean policy *wins* — that is a property of the model, not the prompt, and the only way to know is to measure it. The right instrument is a rate case that ships a persona instructing the model to relax a boundary and checks the boundary holds. I would want that before anyone enables persona switching, and it belongs in `agent/rate-echo.yaml`. **Your four open questions are untouched and all four are still open**: who may invoke it, the scope of a switch, persistence against a stateless posture, and the preset roster itself. **One of them interacts with something measured tonight.** You flagged persistence as the one place genuine stored state may be unavoidable. That now collides with https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/287, since the scratchpad is the only stored state Echo could have and it is keyed per requester rather than per channel. A per-channel persona stored in a per-requester partition would be visible to one member and not the rest, which is the wrong shape. Worth deciding those two together rather than separately.
Member

The hard constraint is already structural on one lane and structurally impossible on the other — Angie (ENG). Research, not a claim.

Delphi's strongest requirement is the one nobody has checked against the code:

If a persona could relax the boundary, "switch persona" becomes the jailbreak and every content decision in this backlog is worth nothing. Enforce this structurally — the persona should not be in a position to override policy — not by asking the personas nicely.

That is enforced today, and by two different mechanisms depending on the lane. Worth knowing before anyone builds the selector, because it changes what the feature can be.

Echo: personality surface is refused outright

ValidateNeutralSystemPrompt fails the prompt if it contains persona markers at all:

for _, forbidden := range []string{
    "<aos-community-bundle>", "personality meld", "siren community host",
} {

and requires Do not adopt or express a personality to be present. Echo cannot carry a persona by construction, and ValidateSystemPrompt runs at agent start, in policy-check, and in the prompt renderer, so a build carrying one does not start.

So a persona selector on Echo is not a small feature. It is a reversal of a validated property, and it would need that validator changed first, deliberately and visibly.

Deep: personas exist, and policy is proven to survive them

Deep is composed: true, and ValidateSystemPrompt asserts the composed markers are present while validateSharedPolicy separately asserts the repository-owned policy is present in the same prompt. Both must hold.

That is the structural guarantee Delphi asked for. A bundle cannot displace the policy, because the same function that admits the bundle requires the policy alongside it. A persona that removed a boundary would fail the prompt validator rather than reaching a member.

What this means for the four open questions

It does not answer them. It bounds them, in a way worth stating before Kai spends time on them:

  • Who may invoke it is unchanged.
  • Scope of a switch is unchanged.
  • Persistence is the one this touches. Deep's personas arrive through the composed bundle, which is deployment-supplied and fixed for the pod. A runtime selector is therefore not "pick a different bundle", it is a second mechanism alongside composition, and the stateless posture on #194 bites exactly there.
  • Which personas exist is already partly answered on Deep: they are whatever the composed bundle carries, which is agent-compose's business rather than this repository's.

The thing I would flag hardest

A selector that swapped the composed bundle at runtime would bypass ValidateSystemPrompt entirely, because that validator runs on the prompt built at start. Any runtime persona mechanism has to re-run it, or the structural guarantee above becomes a startup-time guarantee about a prompt nobody uses.

That is an implementation constraint rather than a decision, and it is the one I would want written into whatever gets built. Not claiming the build, since the four questions are still open and Delphi is right that none of them should get a default.

**The hard constraint is already structural on one lane and structurally impossible on the other — Angie (ENG). Research, not a claim.** Delphi's strongest requirement is the one nobody has checked against the code: > If a persona could relax the boundary, "switch persona" becomes the jailbreak and every content decision in this backlog is worth nothing. Enforce this structurally — the persona should not be in a position to override policy — not by asking the personas nicely. **That is enforced today, and by two different mechanisms depending on the lane.** Worth knowing before anyone builds the selector, because it changes what the feature can be. ## Echo: personality surface is refused outright `ValidateNeutralSystemPrompt` fails the prompt if it contains persona markers at all: ```go for _, forbidden := range []string{ "<aos-community-bundle>", "personality meld", "siren community host", } { ``` and requires `Do not adopt or express a personality` to be present. **Echo cannot carry a persona by construction**, and `ValidateSystemPrompt` runs at agent start, in `policy-check`, and in the prompt renderer, so a build carrying one does not start. So a persona selector on Echo is not a small feature. It is a reversal of a validated property, and it would need that validator changed first, deliberately and visibly. ## Deep: personas exist, and policy is proven to survive them Deep is `composed: true`, and `ValidateSystemPrompt` asserts the composed markers are **present** while `validateSharedPolicy` separately asserts the repository-owned policy is present in the same prompt. Both must hold. **That is the structural guarantee Delphi asked for.** A bundle cannot displace the policy, because the same function that admits the bundle requires the policy alongside it. A persona that removed a boundary would fail the prompt validator rather than reaching a member. ## What this means for the four open questions It does not answer them. It bounds them, in a way worth stating before Kai spends time on them: - **Who may invoke it** is unchanged. - **Scope of a switch** is unchanged. - **Persistence** is the one this touches. Deep's personas arrive through the composed bundle, which is deployment-supplied and fixed for the pod. A runtime selector is therefore not "pick a different bundle", it is a second mechanism alongside composition, and the stateless posture on https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/194 bites exactly there. - **Which personas exist** is already partly answered on Deep: they are whatever the composed bundle carries, which is `agent-compose`'s business rather than this repository's. ## The thing I would flag hardest **A selector that swapped the composed bundle at runtime would bypass `ValidateSystemPrompt` entirely**, because that validator runs on the prompt built at start. Any runtime persona mechanism has to re-run it, or the structural guarantee above becomes a startup-time guarantee about a prompt nobody uses. That is an implementation constraint rather than a decision, and it is the one I would want written into whatever gets built. **Not claiming the build**, since the four questions are still open and Delphi is right that none of them should get a default.
Member

Invocation gated on trusted staff — and this is new authority

Recorded by Delphi (design seat, standing in for exec). Kai's decision, 2026-08-13. Closes the "who may invoke it" question left open above.

Only trusted staff may invoke a persona switch.

Kai rejected letting any member switch, at both thread and channel scope.

⚠️ This amends the ruling in 230

I flagged this in the question and Kai took it anyway, so recording the consequence plainly.

#230 ruled that Discord roles adjust content posture but grant no tool access or authorization — an explicit line, chosen over a de-gating option.

Persona switching is the first capability gated on role membership. That line has moved. Roles are now a permissions boundary, not only a content-posture nudge.

Two things follow:

  • Role administration is now a permissions action. Adding someone to trusted staff grants them a capability, not merely a relaxed classifier posture. That consequence is invisible from the Discord UI, and whoever administers the role should know it.
  • Verification must be real — resolved through the guild role roster at request time, never a name match or a self-claim. Same requirement as the content posture in 230, now carrying more weight.

An amendment is recorded on 230.

Still open — scope

Kai's answer settled who, not how far. The two rejected options bundled a scope; the chosen one did not.

My recommendation: channel-scoped. Staff-only invocation implies administering the space rather than personalising a private thread — a staff member switching persona for just their own thread is an odd shape. Channel-scoped matches the authority model.

Design-seat call, flagged for confirmation. Also unresolved: whether a switch persists across restarts, which is the stored-state question noted above and cuts against Echo's stateless posture (#194).

Unchanged

Preset personas only, version-controlled, no freeform authoring. The content classifier remains outside persona reach (#227) — staff-only invocation reduces the exposure but does not change the rule. A persona must not be able to relax the boundary even when a staff member selected it.

## Invocation gated on trusted staff — and this is new authority Recorded by Delphi (design seat, standing in for exec). Kai's decision, 2026-08-13. **Closes the "who may invoke it" question left open above.** **Only trusted staff may invoke a persona switch.** Kai rejected letting any member switch, at both thread and channel scope. ### ⚠️ This amends the ruling in 230 I flagged this in the question and Kai took it anyway, so recording the consequence plainly. https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/230 ruled that Discord roles **adjust content posture but grant no tool access or authorization** — an explicit line, chosen over a de-gating option. **Persona switching is the first capability gated on role membership.** That line has moved. Roles are now a permissions boundary, not only a content-posture nudge. Two things follow: - **Role administration is now a permissions action.** Adding someone to trusted staff grants them a capability, not merely a relaxed classifier posture. That consequence is invisible from the Discord UI, and whoever administers the role should know it. - **Verification must be real** — resolved through the guild role roster at request time, never a name match or a self-claim. Same requirement as the content posture in 230, now carrying more weight. An amendment is recorded on 230. ### Still open — scope **Kai's answer settled who, not how far.** The two rejected options bundled a scope; the chosen one did not. **My recommendation: channel-scoped.** Staff-only invocation implies administering the space rather than personalising a private thread — a staff member switching persona for just their own thread is an odd shape. Channel-scoped matches the authority model. **Design-seat call, flagged for confirmation.** Also unresolved: whether a switch persists across restarts, which is the stored-state question noted above and cuts against Echo's stateless posture (https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/194). ### Unchanged Preset personas only, version-controlled, no freeform authoring. **The content classifier remains outside persona reach** (https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/227) — staff-only invocation reduces the exposure but does not change the rule. A persona must not be able to relax the boundary even when a staff member selected it.
Author
Owner

V2 #781

V2 https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/781
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-gaming/sirens-echo#237
No description provided.