feat(persona): read the claim-type table from the agent - #196
Merged
Conversation
The payoff for `persona/claim-types/list`: the console stops shipping its own copy of the table. The copy was correct — that was never the problem. A copy of a table two repositories do not own costs a re-sync PR against each on every change, and can only describe the tokens its own build knew about. Resolution moves to `@openvtc/pnm-core/persona` beside the served table, and the strictness orderings come from the agent, so "more protective" means the same thing on both sides. An axis value this build does not recognise is treated as most protective, never least. #195's work is preserved as-is: `treatmentFor` still applies the holder's own decision over the registry's answer, only the axis they decided moves, and a declared token's mask never does. What changed underneath it is where the registry's answer comes from — `isRegisteredType` is now the served table's walk rather than a local one over a compiled record. `null` is a real state and fails closed: masked, `unregistered`, attributed to the registry rather than the holder, with no compiled fallback — a stale copy resolving a token the agent has since tightened is the failure the registry exists to end. A render test pins it, found by leaving the fixture out and watching the reveal-control count go 3 → 4. Drawing stays here, because §3.3 gives the style and says the width is the client's. `listClaimTypes` takes no contextId: the payload is empty and the task is reachable by any authenticated caller, so the holder's own tooling can read it. Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
stormer78
force-pushed
the
feat/read-claim-types-from-agent
branch
from
September 8, 2026 11:14
e4d167c to
105bd2c
Compare
Contributor
Author
|
Rebuilt on top of #195 rather than resolving the conflict textually — it landed in the same files and a mechanical merge would have dropped it. Branch reset to Correcting one claim from the earlier description: I had written that this fixed the §3.3 masking bug — #195's override layer is preserved as-is: 976 tests, 0 failures. |
This was referenced Sep 8, 2026
Merged
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.
The payoff for
persona/claim-types/list: the console reads the claim-typetable from the agent instead of shipping its own.
The copy was correct. That was never the problem.
Worth being precise, because "removed a vendored table" sounds like a bug fix
and this mostly is not one.
manager/claim-sensitivity.tsimplemented §4faithfully, prefix walk included.
What a copy costs is a re-sync pull request against two repositories on every
registry change — the argument
CLAIM-TYPES.md§1 makes for the registryexisting at all, one layer down — and it can only ever describe the tokens its
own build knew about. An agent serving an extension type is invisible to a
client shipping its own table.
Rebuilt on top of #195, not merged with it
#195 (
the holder decides what happens to a value) landed in these same fileswhile this was open, and a textual conflict resolution would have quietly
dropped it. This branch was reset onto
mainand the change re-applied on topof its code instead.
Two things worth saying plainly:
#195 already fixed the §3.3 masking bug, independently and before this — the
maskedFactearly return onsensitivity !== "high"that drewemail.*in theclear. An earlier draft of this PR claimed that fix; it is not mine and the
claim is withdrawn.
#195's override layer is preserved unchanged.
treatmentForstill appliesthe holder's own
sensitivityover the registry's answer, still moves only theaxis they decided, and still leaves a declared token's mask alone — the
narrowness that PR argued for. What changed underneath it is where the
registry's answer comes from:
isRegisteredTypeis now a walk over the servedtable rather than a local one over a compiled record.
What moved where
Resolution →
@openvtc/pnm-core/persona.resolveTreatmentis §4 in full,in one place, next to the served table. The parts that look like detail are the
parts that get got wrong: dot boundaries, a proper prefix,
x:borrowingnothing, and a prefix that can only tighten. A caller walking prefixes itself
has to get all four right and the loosening direction fails quietly.
Orderings come from the agent's
strictness, not a constant here — so "moreprotective" means the same thing on both sides, and a maintainer adding a
stricter mask style is honoured without a rebuild. An axis value this build does
not recognise is treated as most protective.
Drawing stays in the console, because §3.3 gives the style and says the
width is the client's: a run that tracked the length would publish it.
nullis a real state, and it fails closedWhile the table is in flight every value resolves to the floor and is masked;
grouping answers
unregistered. There is deliberately no compiled fallback —that is the copy this replaced, and a stale copy resolving a token the agent has
since tightened is the failure the registry exists to end.
The render fixture proved this by accident: before I passed a registry, the
"one control per hidden attribute" test went 3 → 4, because everything had
fallen to the floor. That is the fail-closed path, observed.
listClaimTypestakes nocontextIdEvery other task in
core/src/persona/takesPersonaCallerParams, whichrequires one. This must not: the payload is empty and the agent gates it as
reachable by any authenticated caller, precisely so the holder's own tooling —
which has no context to name — can read it. The typechecker caught this the
moment the console called it. A context invented to satisfy a signature is a lie
in an audit trail.
Tests
Moved to core (
persona.claim-types.mjs, 10): the §4 rules, against aserved-shaped fixture — exact entry beats family, a bare
nameis a token, agated family is not escapable, a family entry cannot loosen,
x:borrowsnothing, prefixes are dot-bounded, and an unrecognised axis value never resolves
to "no mask".
Kept in the console (
manager-claim-sensitivity.test.mts, 8): the drawing —fixed-width runs, a value no longer than its own tail,
emailLocalrefusing toguess at a non-address, and the §3.3 regression as its own case.
Reframed (
manager-attribute-family.test.mts): "every root the registrydeclares is placed" cannot survive reading the table from the agent — a
maintainer may serve a family this build predates, and
unregisteredis thehonest answer for one, not a gap. It now asserts the mapping is internally
complete (
PLACED_ROOTS), plus a new case pinning the unknown-family answer.950 tests across four workspaces, 0 failures.
tsc -bandnpm run buildclean.
Guide checklist (§9)
claim-types/listshipped in the agent (VTI #1315) before this reads it, andtask-surface.jsonrecords it