Resolve Wire Ambiguity Between OFFER_CASE_MANAGER_ROLE and OFFER_CASE_OWNERSHIP_TRANSFER via Dedicated Object Type
Context and Problem Statement
OFFER_CASE_MANAGER_ROLE and OFFER_CASE_OWNERSHIP_TRANSFER both serialize as
Offer(VulnerabilityCase) on the wire. They are currently disambiguated only by
the presence of target=CaseParticipant in the case-manager-role offer and its
absence in the ownership-transfer offer.
A minimally-conformant or buggy sender that omits the target field would have
its case-manager-role offer silently dispatched as an ownership-transfer offer
(or vice versa) with no error surfaced. The SEMANTIC_REGISTRY ordering guard
(_validate_registry_order()) prevents misrouting within a single implementation
but does not protect against malformed inbound activities from remote peers.
Issue: CONCERN-1674.
Decision Drivers
- Wire shapes should be self-describing; disambiguation must not depend on registry ordering alone.
- The
targetfield as a discriminator is semantically odd: the conceptual target of a role offer is the Actor receiving the role, not the CaseParticipant wrapper record. - A general-purpose role-offer mechanism (offering any
CVDRoleto any Actor in a Case context) is more expressive than a CASE_MANAGER-specific flow. - Backward compatibility: the existing
OFFER_CASE_MANAGER_ROLEwire format must be deprecated in a traceable way.
Considered Options
- New
as_CaseParticipantRoleobject type — introduce a dedicated wire object carrying aCVDRolevalue; wire shape becomesOffer(CaseParticipantRole, target=Actor, context=VulnerabilityCase). - Keep
Offer(VulnerabilityCase); addstrict=Trueto the pattern — enforce the target discriminator at the pattern layer without a type change. Partially adopted as an implementation complement to Option 1 (CONCERN-2322):ActivityPattern._match_activity_fieldhardcodesstrict=Falsefortarget_by default; any pattern usingtarget_as the sole discriminator MUST setstrict=Trueso bare URI strings cannot match typed target constraints. See SE-08-004. - Spec-and-notes documentation only — record the ordering requirement and the target-as-discriminator rationale; no wire format change.
Decision Outcome
Chosen option: Option 1 — new as_CaseParticipantRole object type, because:
- It eliminates the structural ambiguity; the wire shape is self-describing regardless of registry ordering or sender compliance.
- A general
Offer(CaseParticipantRole, target=Actor, context=VulnerabilityCase)is more semantically correct than targeting a CaseParticipant wrapper, and generalises naturally to offering anyCVDRole(VENDOR, COORDINATOR, etc.) rather than only CASE_MANAGER. - Options 2 and 3 leave the latent misrouting risk in place for external senders.
Implementation complement (CONCERN-2322): Option 2's strict=True mechanism
is also applied as a structural hardening: ActivityPattern._match_activity_field
now respects self.strict for the target_ field pair rather than hardcoding
False, so bare URI strings cannot match typed target constraints when
strict=True. This closes the gap where registry ordering was the sole
protection against misrouting even with Option 1 in place. Additionally:
OFFER_CASE_MANAGER_ROLE was removed entirely (not merely deprecated) since
its wire format was never emitted by any supported actor, and
ACCEPT_CASE_PARTICIPANT_ROLE / REJECT_CASE_PARTICIPANT_ROLE were added to
complete the three-way role-offer flow. See SE-08-004, SE-08-005.
Consequences
- Good, because wire shapes for role delegation and ownership transfer are structurally distinct and self-describing.
- Good, because the general role-offer mechanism supports future role delegation flows beyond CASE_MANAGER.
- Good, because
target=Actoris semantically correct; the target of a role offer is the Actor receiving the role, not the CaseParticipant wrapper. - Bad, because this is a breaking wire format change for
OFFER_CASE_MANAGER_ROLE; existing devlogs and interop partners must be migrated. - Bad, because it requires a new
VultronObjectTypeenum value, new vocab class, updated factory, updated pattern, and new use case + BT handler.
Validation
- Import-time
_validate_registry_order()guard confirms no less-specificOffer(VulnerabilityCase)pattern precedes the newOffer(CaseParticipantRole)pattern inSEMANTIC_REGISTRY. - Unit tests in
test/test_semantic_activity_patterns.pyassert correct dispatch. - Demo scenario emits the new wire format and the invariant harness validates it.
More Information
SE-08-001throughSE-08-002mandate registry ordering and required target field for any pattern that discriminates by target.SE-08-003records the SHOULD preference for dedicated object types over target-field discrimination.SE-08-004mandatesstrict=Trueon patterns that usetarget_as the sole discriminator (amended by CONCERN-2322).SE-08-005recorded the removal of theOFFER_CASE_MANAGER_ROLEbackward-compat format (CONCERN-2322).- Implementation issues: tracked as GitHub Task issues blocked by CONCERN-2322.
Generated spec requirements: specs/semantic-extraction.yaml SE-08-001,
SE-08-002, SE-08-003, SE-08-004, SE-08-005.