refactor(persona): speak the vocabulary a person reads - #280
Merged
Conversation
The identity pane called a persona DID a "face". Nothing else does: not the Trust Tasks spec, not the VTA, not the SDK, not `pnm persona binding set --persona-did`, not the console pane shipped this week, and not this repo's own `PersonaRecord` / `persona_ref` / `persona_did`. A holder moving between two of those surfaces had to translate, and translation is where a boundary gets misread. "Profile" was the other candidate and it is the one that would have hurt: it already names the thing a persona *presents*. Calling the DID a profile would have collided head-on with `persona/profile/*` — you bind a profile to a persona; the reverse is not a sentence. The word "persona" is genuinely overloaded — the task family spans the DID and everything above it — and "face" was an attempt to fix that by inventing a third noun. The overload is a prose problem with a prose fix: say "persona DID" where a sentence has to be unambiguous about which layer it means, exactly as `pnm`'s own help does. The pane's module header now states both words and the direction between them. `ContentPanelState::personas` becomes `identity`, so the list can take the name it should have had: `identity.personas` rather than `personas.personas`. That matches the menu entry, which has read "My Identity" since the pane landed — the pane is not only personas, and now the field says so. Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
`design-docs/persona-vocabulary.md` fixes one set of words across the console, `pnm`, the mobile agent and this TUI, so a person meets the same sentence wherever they meet the model. This brings the identity pane to it. The substitutions that matter: - an **attribute** is a **fact** — "a fact about you, held once"; - a **profile** is a **face** — "the set of facts you show together". Not "profile": that already means three things in this product and "my LinkedIn page" to everyone else; - a **binding** is **wearing** — a persona wears a face in a community. `Change face`, `Take it off`; - **disclosures** are **what has left, and to whom**; **correlation** is **linked**; provenance reads *you said so* / *credential* / *made per verifier*, and the credential row carries the half people miss — the same signature everywhere, so it links you. Tabs: Personas · Your facts · Faces · Communities · What has left. The spec's words are exact and stay in the types, on the wire and in the audit log. They are kept off the *screen*: `PersonaTab::Profiles` still addresses `persona/profile/*`; its label says Faces. That split is the whole design of the table, and it is why this is a display-layer change rather than a rename. `stale` now carries its reason (`stale · revoked`) — the table asks for it, and "stale" alone tells someone something is wrong without telling them what. The agent was already sending `staleReason`; nothing was reading it. ## The guard A test renders every tab — empty and populated — plus all three editors, and fails if any word from the table's avoid-list reaches the screen. Every one of them is a word this pane's own code and wire format use, so they are one careless `format!` away at all times, and the drift is invisible in review because each line looks correct to whoever wrote it. Verified by flipping a label back: the test fails, rather than passing vacuously. Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
A scripted replacement matched the same `use` line in two tests and added the import to both. Clippy runs with `-D warnings` in CI, so an unused import there is a failed build. Ran `cargo clippy --workspace --all-targets -- -D warnings` this time rather than plain clippy — the plain form said nothing, which is how it reached CI. Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
The rebase onto #279 put the grant hint inside the pane the vocabulary work had just rewritten, and the widened guard immediately found three words on screen that the table keeps off it — "pool" and "profiles" in the hint's own prose, and "holder" in the agent's refusal, which the pane was echoing verbatim. The hint's prose is ours, so it now says facts and faces. The agent's sentence is not ours, and echoing it was the wrong call rather than a wording slip. It is accurate and abstract — "the holder's attribute pool, which sits above every trust context" — and it lands on someone who has just been refused, which is the worst moment to meet three unfamiliar nouns. This is the one failure the pane recognises exactly, so it now answers it in the agreed words and gives the command. Every other failure is still echoed verbatim: that text is *data*, naming a host or a contract mismatch, and translating what we do not recognise would be inventing a cause (R6.4). The guard grew the state that would have caught this: every tab now renders refused as well as empty and populated. The words written for a failure are the ones written under pressure, and nothing else on screen exercised them. `persona-holder` inside the command is exempted — a capability name an operator types verbatim, like a task URI. The exemption is the exact token, so a sentence cannot hide behind the hyphen. Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
stormer78
force-pushed
the
feat/persona-vocabulary
branch
from
September 7, 2026 12:19
b61f81c to
c25cd34
Compare
stormer78
added a commit
that referenced
this pull request
Sep 7, 2026
#281) #280 brought the identity pane to `design-docs/persona-vocabulary.md` and left the persona copy elsewhere for when it was next touched. This is that copy: the join flow's identity choice, its reuse warning, the invitation list, and the communities panel's empty state. The reuse warning is the one that matters. It is the last thing a person reads before linking themselves across two communities, and it now carries the sentence the table asks every surface to share verbatim — "the same person to anyone who sees both" — rather than a paraphrase of it. The point of a fixed phrase is that meeting it twice teaches it; a paraphrase in one surface spends the recognition and gives nothing back. ## What was deliberately left alone `profile` in setup, backup and `--profile`. That is the **config profile** — a local installation profile that predates the persona family, has its own meaning to operators, and appears in flags they type. It is exactly the collision the vocabulary doc cites when it declines to use "profile" for a face, and renaming it here would create the confusion the table exists to prevent. Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Aligns the identity pane with
design-docs/persona-vocabulary.md. Supersedesthe naming half of #278, which guessed at the vocabulary and got the direction
of one word backwards — I had used "face" for the persona DID; the table uses
it for a profile.
Two commits
1.
persona, notface, for a persona DID. That much of #278 survives: thespec, the VTA, the SDK,
pnm, the console pane and this repo's ownPersonaRecordall say persona. Also renamesContentPanelState::personas→identity, so the list can beidentity.personasrather thanpersonas.personas.2. The vocabulary itself.
Change face,Take it offselfAsserted/credentialBacked/generatedTabs read Personas · Your facts · Faces · Communities · What has left.
The spec's words stay in the types, on the wire and in the audit log — they are
kept off the screen.
PersonaTab::Profilesstill addressespersona/profile/*; its label says Faces. That split is why this is adisplay-layer change and not a rename: no core type was renamed.
Two things the table surfaced
stalenow carries its reason (stale · revoked). The table asks for it,and "stale" alone tells someone something is wrong without telling them what.
The agent was already sending
staleReasonand nothing was reading it.credential and same signature everywhere — links you. Severity inverts
intuition here: a credential shown whole links more than a fact you said, and
the words must not hide it.
The guard
the_pane_speaks_the_agreed_vocabularyrenders every tab — empty and populated— plus all three editors, and fails if any word from the table's avoid-list
reaches the screen.
Every one of those words is a word this pane's own code and wire format use, so
they are one careless
format!away at all times, and the drift is invisible inreview because each line looks correct to whoever wrote it. Verified by flipping
a label back to "Profiles": the test fails, rather than passing vacuously.
Two words are deliberately absent from the list: credential is on-screen
vocabulary, and per verifier is the agreed phrase for a generated value —
the table bans "verifier" in prose, not that phrase.
Not done here
openvtc health, the join flow and the communities panel still use the spec'swords on screen (
persona, which is fine; and the communities panel's ownpresents:line, which this replaces withwears:viaBindingSummary::describe). A wider sweep of the setup and join copy is worthdoing when those are next touched — flagged rather than folded in, since it is
outside this pane.
Pre-merge checklist (vti-stack-development-guide §9)
cargo test --workspacegreen (420 in the binary, 500 in core), clippy clean.