feat(manager): a list view over the pool, with multi-select - #210
Merged
Conversation
…eview The identity map answers "what reaches what", one selection at a time. That is the right picture for a pool of five and the wrong one for a pool of fifty: a holder who wants to tidy has to open, read and confirm one card at a time, which is the clunkiness the map cannot design its way out of because one-at-a-time is its whole subject. This is the model for a second view over the same graph — the same `AttributeNode`s, the same families, no new wire records, and a selection that is a set rather than a single node. Out of the component for the reason `identity-graph.ts` is: the interesting parts are the ordering and the set arithmetic, and both are testable with no DOM. **The grouping is the one that already exists.** Rows group by `familyOf` in `FAMILY_ORDER`, with `familyStyle`'s words as headings — not a second taxonomy invented for this view. Two groupings of one pool that disagree is the defect where a person counts six in one place and five in the other. **A range selects over the rendered order, not the pool.** Grouping reorders, so a range computed from the unsorted pool selects attributes that were never between the two rows the person clicked. **One bulk action is deliberately missing.** `persona/attribute/put` is a replace and this console lists without `includeSensitive` on purpose, so a `sensitivity: high` attribute reaches the client with `value: undefined`. A bulk visibility change written through `put` would send an empty value for every sensitive one and blank it — silently, with no `attribute/get` and no version history to restore from. That is the hazard `AttributeEditor` fetches a value to avoid, multiplied by the size of the selection. So `BULK_ACTIONS` has two members, and `whyNoBulkVisibility` exists so the screen can say why rather than leaving a gap someone fills in later. Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
`persona/attribute/delete` refuses an attribute a profile still names unless
`cascade` is set — "a profile silently projecting one fewer claim is a
failure the holder discovers from the far side of a disclosure". A bulk
delete of twelve where five are in use is therefore five refusals, arriving
one at a time, after the seven that already succeeded.
The preview now counts them up front, so the confirm step can ask the second
question before anything is sent — the way `Destructive`'s `force` tick
does. Overriding a refusal the agent makes on purpose is its own decision
and does not follow from pressing Delete.
It reads all three referencing forms, not just live references:
`attributeIds` carries only `{ref}`, while a pin and an override name the
attribute too and the agent refuses on those as well. Counting only live
references would under-count and put the holder back in the
one-refusal-at-a-time state the preview exists to prevent.
`facesAffected` names the faces rather than counting them — "Work loses two"
is the sentence that changes a mind, and it is not available anywhere else
on the screen.
Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
The map's subject is reach — what one selection touches — so it draws one selection at a time. That is the right picture and the wrong tool for the job a holder arrives with once the pool is large: delete these four stale numbers, put these six into a new face. On the map that is opening, reading and confirming one card at a time, and no amount of layout fixes it, because one-at-a-time is what the map is for. So: a second view over the same graph, behind a toggle. Same `AttributeNode`s, same families, same words, no new wire records, and a selection that is a set. Rows group under the family headings the map already uses, headings carry a tri-state checkbox, shift-click takes the range that was on screen, and the bulk bar offers exactly two actions. **`attribute-words.ts` is new and is an extraction, not an addition.** `provenanceWords`, `labelSaysSomethingElse` and `staleWords` were private to `persona-map.tsx` while the map was the only surface drawing an attribute. A second copy is how the map comes to say *you said so* beside a row the list calls *credential · storm.ws* — the vocabulary defect in the channel a person is most likely to act on. **Bulk visibility is not offered and the screen says why.** `attribute/put` is a replace and this pane lists without `includeSensitive`, so a bulk visibility write would blank every sensitive value in the selection. **Delete asks the second question first.** The preview counts what faces still reference, names them, and `cascade` rides on `Destructive`'s existing `force` tick. `AttributeEditor` is keyed on the record it edits, which `manager-form-state.test.mts` caught: it seeds `useState` from `existing`, so an unkeyed second open shows the first attribute's value — and on a put, writes it over the second's. Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
🛡️ AI Agentic Security Code Review🔎 A manual security review is recommended before merging. Please contact the Security team for specifics and remediation guidance.
|
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 map's subject is reach — what one selection touches — so it draws one selection at a time. That is the right picture and the wrong tool for the job a holder arrives with once the pool is large: delete these four stale numbers, put these six into a new face. On the map that is opening, reading and confirming one card at a time, and no amount of layout fixes it, because one-at-a-time is what the map is for.
So this adds a second view over the same graph, behind a Map/List toggle. Same
AttributeNodes, same families, same words, no new wire records — and a selection that is a set rather than a single node.What it does
attribute-family.ts), with a tri-state checkbox per heading.Three things worth reviewing closely
Bulk visibility is deliberately absent, and the screen says why.
persona/attribute/putis a replace, and this pane lists withoutincludeSensitiveon purpose, so asensitivity: highattribute is in hand withvalue: undefined. A bulk visibility change written throughputwould send an empty value for every sensitive row and blank it — silently, with noattribute/getand no version history to restore from.whyNoBulkVisibility()is asserted in a test, so adding a third action means deleting that assertion.Delete asks the second question first. The agent refuses to delete an attribute a face still references unless
cascadeis set. Sending twelve and discovering five are in use is five refusals after seven irreversible successes. The preview counts them up front across all three referencing forms —{ref},{ref, pinVersion},{ref, override}; reading onlyattributeIdsunder-counts — names the faces that would lose an entry, and putscascadeonDestructive's existingforcetick.attribute-words.tsis an extraction, not an addition.provenanceWords,labelSaysSomethingElseandstaleWordswere private topersona-map.tsxwhile the map was the only surface drawing an attribute. A second copy is how the map comes to say you said so beside a row the list calls credential · storm.ws.A bug this found
manager-form-state.test.mtsfailed onAttributeEditorrendered without akey. It seedsuseStatefromexisting, which runs on mount and never again — so opening a second attribute from the list showed the first one's value, and on a put, which replaces, that writes one attribute's value over another's. Fixed withkey={editing.attributeId}.Tests
24 new — 16 model (
manager-attribute-list.test.mts), 8 rendered (persona-list.render.test.mts). Full suite 440/440,tsc -bclean.clickWithis new on the test harness so a modifier click goes throughactlike every other event, rather than a raw dispatch that warns and settles in a different order than a browser would.Not in this PR
Grouping faces into worlds (
persona/facet/*) is a separate chain that starts with a spec change. This needs none of it.