Skip to content

feat(vetting): make a face for this community without leaving the flow - #358

Merged
stormer78 merged 1 commit into
mainfrom
feat/face-for-this-community
Sep 19, 2026
Merged

stormer78 merged 1 commit into
mainfrom
feat/face-for-this-community

Conversation

@stormer78

Copy link
Copy Markdown
Contributor

#356 made the picker say which faces cannot make this community's card. This
handles the case it exposed: none of them can.

The answer then was to leave the vetting flow, build a face under My Identity,
come back and press f again — the out-of-cycle config work this flow has been
folding inline everywhere else (#348, #351).

The row

The picker gains a last row:

This community's card needs  name.legal

▸ OSS Developer  2 attributes  worn now
      shows     email.work, name.preferred
      missing   name.legal — no card for this community
  Work           6 attributes
      shows     name.legal, dob, address.home, …

  Make a face for this community    from attributes you already have

A row rather than a key, so every way out of this screen is in the list the
eye is already on.

The form knows what the face is for

Make a face for this community

  Name              ▸ _

 This community's card needs  name.legal

  [ ] EMAIL.WORK        email.work
▸ [x] NAME.LEGAL        name.legal      needed here
  [ ] DOB               dob

The community has already named the claim types its card must carry, so the
matching attributes open already ticked. The holder's decision is usually
just a name.

What is still missing is computed against the current selection, not the
pool, so unticking something required says so at the moment of the choice
rather than three steps later at the card preview.

Composed, never created

The form selects from attributes the pool already holds and never creates one.
Supplying a value for a legal name is a different act from arranging which
attributes a face shows, and it belongs with the editor that knows about value
types, sensitivity and masking.

What this form owes the holder instead is to name the claim type no attribute
can cover
:

  missing   name.legal — this face could not make the card
            no attribute of yours is name.legal — add one under My Identity

That is a different problem from a claim merely unticked, because ticking harder
will not fix it, and a_claim_the_pool_cannot_cover_is_named_as_such pins that
the two do not get conflated.

This is the deliberate boundary of the PR. A holder who has never recorded
their legal name still goes to My Identity once. The inline flow covers the
common case — attributes exist, spread across faces that individually fall
short — which is exactly the shape the dry run hit.

Three smaller decisions

Creating and wearing are one step. A face made here exists to be worn by
this application; leaving it created but unworn would put the holder back on the
picker to do what they had just asked for. It reuses the FaceWorn outcome, so
the face is recorded on the application (ChosenFace, #356) and displayed by
the same paths as one chosen from the list.

The pool read meets the same holder-grant refusal the faces read does — the
pool is what a face is built over — and routes it to the same view rather than
passing the agent's paragraph through.

other_entries is empty on the write, because this creates. ProfileForm's
own doc warns that rebuilding a profile from only the ticked boxes silently
deletes pinned and inline entries; that hazard is real for the general editor
and absent here, since there is no existing profile to lose anything from.

Testing

  • cargo test --all-features — all green
  • cargo clippy --all-targets --all-features — clean
  • cargo fmt --all — clean
  • RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features — clean

Three new tests: the opening selection, the uncoverable case, and that the pool
read's holder refusal lands on the same view.

Not covered: the FaceJob::Create job itself, which needs a live VTA — the
tests exercise the form's logic and the outcome handling, not the write. The
key wiring (Tab between name and list, Space to tick) is also untested; it goes
through the panel's generic non-List key handler, which has no harness.

Not validated live. Nothing here has been run against a real pool.

#356 made the picker say which faces cannot make this community's card.
That leaves the case where none of them can: the answer was to go to My
Identity, build a face there, come back and press f again — the
out-of-cycle config work this flow has been folding inline everywhere
else.

The picker gains a last row, "Make a face for this community". A row
rather than a key, so every way out of the screen is in the list the eye
is already on.

It opens a form that knows what the face is for. The community has
already named the claim types its card must carry, so the matching
attributes open **already ticked** and the holder's decision is usually
just a name. What is still missing is said against the current
selection, not against the pool, so unticking something required says so
at the moment of the choice rather than at the card preview.

Composed from attributes the pool already holds; it never creates one.
Supplying a value for a legal name is a different act from arranging
which attributes a face shows, and it belongs with the editor that knows
about value types, sensitivity and masking. What this form owes the
holder is to name the claim type no attribute can cover — a different
problem from one merely unticked, because ticking harder will not fix
it — rather than offer a face that cannot make the card.

Creating and wearing are one step. A face made here exists to be worn by
this application; leaving it created but unworn would put the holder
back on the picker to do what they had just asked for. It reuses the
`FaceWorn` outcome, so the face is recorded on the application and shown
by the same paths as one chosen from the list.

The pool read meets the same holder-grant refusal the faces read does —
the pool is what a face is built over — and routes it to the same view
rather than passing the agent's paragraph through.

`other_entries` is empty on the write because this creates: there is no
profile whose pinned or inline entries could be dropped. The general
editor has to carry them; here there is nothing yet to carry.

Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
@stormer78
stormer78 requested a review from a team as a code owner September 19, 2026 09:55
@stormer78
stormer78 merged commit 58d99ed into main Sep 19, 2026
15 checks passed
@stormer78
stormer78 deleted the feat/face-for-this-community branch September 19, 2026 09:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant