feat(conductor): principalRef identity-bridge for channel-binding delivery (P2a)#389
Merged
Merged
Conversation
…ivery (P2a) Conductor reminders/approvals are delivered by looking up a channel binding keyed by the human-step principal / role-holder id, but the binding was keyed by the channel-native id (Teams AAD object id) while operators address people by email, so delivery silently missed. This threads an operator-addressable `principalRef` through the routines turn-capture seam and keys the binding by it. - plugin-api: add optional `principalRef?` to captureRoutineTurn's info (additive contract; the only OSS caller is integration.ts, so no compiled plugin breaks). - routines/integration.ts: forward principalRef through onTurnCaptured. - index.ts: key the binding upsert via bindingKeyForTurn (principalRef || userId -- `||` so a blank principalRef falls back instead of writing an empty key). - principalId.ts: canonicalizePrincipalId (trim + lowercase) + bindingKeyForTurn. - channelBindingStore: canonicalize the key on BOTH write (upsert) and read (get/getMany) so casing/whitespace between the channel-supplied key and an operator-typed holder id can never cause a silent `unreachable`; getMany returns results keyed by the caller's original holder id. Inert until a channel sets principalRef (the Teams plugin half, P2b, follows once this merges). Reviewed (Claude + Forge APPROVE-WITH-CHANGES): folded the normalization mismatch (the make-or-break), the empty-string ??->|| gap, the stale identity-contract comment, and the test import + keying coverage. Build + lint + 54 conductor/routines tests green.
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.
Conductor identity-bridge:
principalReffor reminder/approval delivery (P2a)The Conductor delivers proactive reminders/approvals by looking up a channel binding keyed by the human-step principal / role-holder id. But the binding is keyed by the channel-native id (for Teams, the AAD object id) while operators address people by email (role holders are entered as
holder@email). So delivery silently misses and the await is flaggedunreachable.This is the OSS half of the fix. It threads an operator-addressable
principalRefthrough the routines turn-capture seam so a channel plugin can supply the email, and keys the binding by it.Changes
@omadia/plugin-api— add optionalprincipalRef?: stringtocaptureRoutineTurn's info. Purely additive; the only OSS caller isroutines/integration.ts, so no compiled plugin breaks.routines/integration.ts— forwardprincipalRefthrough theonTurnCapturedobserver.index.ts— key the binding upsert viabindingKeyForTurn(info)=principalRef || userId(||, so a blankprincipalReffalls back instead of writing an empty, never-matched key).conductor/principalId.ts(new) —canonicalizePrincipalId(trim + lowercase) +bindingKeyForTurn.conductor/channelBindingStore.ts— canonicalize the key on both write (upsert) and read (get/getMany), so casing/whitespace differences between the channel-supplied key and an operator-typed holder id can never cause a silentunreachable.getManyreturns the result keyed by the caller's original holder id.Staging
Inert until a channel actually sets
principalRef— the Microsoft Teams plugin half (resolve + pass the user's email) follows in a private-repo change once this merges, mirroring how the channel event-emit plugin work staged behind #388.Review & tests
Double-reviewed (independent correctness/cleanup pass + an adversarial Codex pass, APPROVE-WITH-CHANGES). Folded: the normalization mismatch (the make-or-break for delivery), the empty-string
??→||gap, a stale identity-contract comment, and test import + keying coverage. Build + lint clean; 54 conductor/routines tests pass incl. new coverage for the forwarding, the canonicalization (case-insensitive match), andbindingKeyForTurn.