Story-2916

Uncurated matter types no longer dead-end create

Design X keeps the read contract stable and fixes the create validator. A matter type with no live offered client-side participant labels now accepts any live participant label from the caller's firm. A curated matter type still admits only its live attached client-side set.

The validator decision

POST /app/matters
        │
        ▼
query live client-side labels offered by the selected matter type
        │
        ├─ non-empty → use that offered set
        │
        └─ empty ───→ use the live participant-label catalogue for this firm
                       (covers no mappings and archived-only mappings)

Match the "us" party by id or case-folded name.

No match on either path → existing
pramaan.matter.participant_label_not_allowed validation error

Acceptance behavior

ScenarioObserved result
PLM-02 / PLM-03: uncurated type + live firm catalogue label Create returns 201 for both participantLabelId and participantLabelName.
Type whose only client-side mapping points to an archived label Read returns an empty offered set; create accepts a live firm catalogue label with 201.
PLM-05: curated type + non-offered live label Create returns the existing 422 pramaan.matter.participant_label_not_allowed.
PLM-24: empty catalogue + advisory posture Create returns 201 without a participant label.
Empty catalogue + adversarial label attempt Create returns the existing 422 domain validation error; no 500 and no matter row.

Why the response contract stays unchanged

MatterTypeParticipantLabelService.list_client_side_labels, every DTO, and every route response remain untouched. The read endpoint still returns the attached set, including an empty list for an uncurated type. The app-lane consumer can detect that empty list and use the existing participant-label catalogue endpoint without new codegen.

Red-first and verification evidence

Files