Skip to main content

Conflict API

Table of Contents

Top

upsquad/conflict/v1/conflict.proto

AcknowledgeConflictRequest

AcknowledgeConflictRequest moves status to 'acknowledged'.

FieldTypeLabelDescription
conflict_idstringconflict_id is the UUID primary key.

AcknowledgeConflictResponse

AcknowledgeConflictResponse returns the updated conflict.

FieldTypeLabelDescription
conflictConflictconflict is the updated row state.

Conflict

Conflict is a single policy_conflicts row with its associated notification delivery ledger.

FieldTypeLabelDescription
conflict_idstringconflict_id is the UUID primary key of the policy_conflicts row.
org_idstringorg_id scopes the conflict to a single tenant.
action_typestringaction_type is the governance action_type that produced the conflict (e.g. 'tool_call', 'llm_request').
targetstringtarget is the fine-grained target within action_type ('*' for wildcard, e.g. specific tool slug).
member_idstringmember_id is the member the cascade was evaluated against. Empty string when the conflict is org-wide (no member context).
winning_policyConflictLayerwinning_policy describes the layer + policy id that was returned to the caller (sticky-deny + tightest-first).
losing_policyConflictLayerlosing_policy is the layer + policy id whose verdict was suppressed. losing_policy.policy_id is always populated; losing_policy.layer may be empty when the losing verdict was the platform default.
clearance_at_decisionint32clearance_at_decision captures the numeric clearance (L1-L5) that applied when the decision was taken.
suggested_merge_jsonstringsuggested_merge_json is populated by the MergeSynthesisWorker once the LLM has proposed a merged policy. Empty string == pending synthesis; non-empty is a JSON object literal.
notified_partiesNotifiedPartyrepeatednotified_parties is the recipient set computed at conflict creation.
statusConflictStatusstatus is the lifecycle state.
resolved_bystringresolved_by is the member id that transitioned status to resolved or dismissed. Empty string until that transition occurs.
resolved_atgoogle.protobuf.Timestampresolved_at is the timestamp of the resolve/dismiss transition.
created_atgoogle.protobuf.Timestampcreated_at is the UTC timestamp of the conflict row insertion.
deliveriesNotificationDeliveryrepeateddeliveries is the per-channel delivery ledger. Populated on reads for the detail view; omitted on stream events for payload size.
sidesConflictSiderepeatedsides is the side-by-side diff view of the cascade trace (GAP-26, #1104). The server-side translator derives sides[] from the existing cascade-trace structure (winning_policy + losing_policy + clearance_at_decision + suggested_merge_json metadata) so existing readers that don't consume sides[] keep working unchanged.

For the v1 translator each conflict produces exactly two sides — winning + losing. The side_id values are stable strings ("winning", "losing") that ResolveConflict.SIDE_WITH callers pass back via ResolveConflictRequest.side_id (GAP-28, #1106). | | cached_recommendation | Recommendation | | cached_recommendation is the most-recent GetRecommendation result (GAP-27, #1105) persisted on the conflict row. Empty until the first GetRecommendation call. Surfaced here so the redesigned Conflicts page can render "Quad recommends:" without an additional round-trip after the conflict listing. |

ConflictLayer

ConflictLayer pairs a cascade layer identifier with the policy id that matched at that layer.

FieldTypeLabelDescription
layerstringlayer is one of 'platform', 'org', 'org_unit', 'member'.
policy_idstringpolicy_id is the UUID of the matching policy row. Empty string only for platform-default "policies" which do not have a row.

ConflictSide

ConflictSide is a single side of the side-by-side diff view rendered by the redesigned Conflicts page (GAP-26, #1104). The shape mirrors the prototype (upsquad-client/docs/design/redesign-2026-04/project/ conflicts.jsx) so the JSX consumer can render with no field rename.

FieldTypeLabelDescription
side_idstringside_id is a stable, non-localised identifier used by ResolveConflict.SIDE_WITH (#1106) to refer back to this side. The v1 translator emits the constants "winning" / "losing" mirroring the cascade outcome.
sourcestringsource is the human-readable origin of the side (e.g. "GR-01 · Refund ceiling", "Marcus R · Finance Lead").
verdictstringverdict is the short summary the side delivers (e.g. "Block", "Allow", "Approved", "Pause agent").
reasonstringreason is the longer-form justification rendered in the diff body.
colorSideColorcolor is the rendering hint for the side card. Mirrors the prototype's color enum.
weightstringweight is the rendering hint for the side's "weight" footer (e.g. "Hard rule", "Pattern match · 87% conf", "Step 3 · final"). It is a string to keep the contract free-form — the translator picks a phrasing that matches the cascade context. Empty when not known.
whoPersonwho is populated when the side originates from a named individual approver (vs. a policy rule). Optional — left null for hard-rule sides where the source IS the rule.

GetConflictRequest

GetConflictRequest fetches a single row by id.

FieldTypeLabelDescription
conflict_idstringconflict_id is the UUID primary key.

GetConflictResponse

GetConflictResponse wraps the fetched conflict.

FieldTypeLabelDescription
conflictConflictconflict is the fetched row with its delivery ledger.

GetRecommendationRequest

GetRecommendationRequest fetches (or generates + caches) the platform "Quad recommends:" recommendation for a single conflict (GAP-27, #1105).

FieldTypeLabelDescription
conflict_idstringconflict_id is the UUID primary key of the conflict.
force_refreshboolforce_refresh ignores any cached recommendation and re-runs the LLM call. Defaults to false so the typical UI flow doesn't re-bill.

GetRecommendationResponse

GetRecommendationResponse returns the recommendation and a flag indicating whether it was served from cache.

FieldTypeLabelDescription
recommendationRecommendationrecommendation is the verdict + target + reasoning + confidence bundle. Same shape that is also surfaced on Conflict.cached_recommendation after the first GetRecommendation call so the listing endpoint can render the recommendation card without an extra round-trip.
cachedboolcached is true when the response was served from the recommendation cache on the conflict row, false when a fresh LLM call was made.

ListConflictsRequest

ListConflictsRequest drives the paginated admin inbox query.

FieldTypeLabelDescription
statusConflictStatusstatus filters by ConflictStatus. Zero value returns all statuses.
page_sizeint32page_size caps the number of rows returned (default 50, max 100).
page_tokenstringpage_token is an opaque cursor returned by the previous call.
member_idstringmember_id filters to conflicts involving the given member. Empty string means "any member".

ListConflictsResponse

ListConflictsResponse is the paginated result set.

FieldTypeLabelDescription
conflictsConflictrepeatedconflicts are the page results ordered created_at DESC.
next_page_tokenstringnext_page_token is opaque; empty string on the final page.

NotificationDelivery

NotificationDelivery is a single row from conflict_notifications — one per (conflict, recipient, channel) fan-out attempt.

FieldTypeLabelDescription
channelstringchannel is one of 'quad', 'bell', 'audit', 'inbox', 'email', 'webhook'.
statusstringstatus is one of 'pending', 'delivered', 'failed', 'skipped'.
delivered_atgoogle.protobuf.Timestampdelivered_at is set when status transitions to 'delivered'.
failed_atgoogle.protobuf.Timestampfailed_at is set when status transitions to 'failed'.
failure_reasonstringfailure_reason is the operator-actionable error message when the channel adapter fails.
kindstringkind differentiates 'conflict.new', 'conflict.merge_ready', and 'conflict.resolved' dispatches.
recipient_idstringrecipient_id is the member the delivery targeted.

NotifiedParty

NotifiedParty is a single recipient the 6-channel notifier fanned out to.

FieldTypeLabelDescription
member_idstringmember_id is the recipient's member UUID.
scopestringscope describes why the member was notified (e.g. 'policy_author', 'supervisor', 'admin').
reasonstringreason is a free-form human-readable annotation.

Person

Person identifies a named individual approver associated with a side. Only used by ConflictSide.who today; kept top-level so future GAPs can re-use the same wire shape.

FieldTypeLabelDescription
namestringname is the display name (e.g. "Marcus R").
rolestringrole is the person's role at decision time (e.g. "Finance Lead").

Recommendation

Recommendation is the cached "Quad recommends:" verdict surfaced by GetRecommendation (GAP-27, #1105) and cached on the conflict row.

Shape matches recommendation in the JSX prototype so the front-end can render verdict + optional target ("to <role>") + reasoning without remapping. confidence is a 0..1 float supplied by the LLM router and surfaced for the "87% conf" badge variant.

FieldTypeLabelDescription
verdictstringverdict is the short imperative the recommendation advocates (e.g. "Escalate", "Adopt Priya's verdict", "Request budget extension").
targetstringtarget is the optional addressee for the recommendation (e.g. "Finance Lead", "CFO"). Empty when the recommendation does not target a named role.
reasoningstringreasoning is the longer-form justification rendered under the verdict in the recommendation card.
confidencefloatconfidence is the LLM router's 0..1 confidence score for the recommendation. Zero when the underlying router does not surface a confidence signal.
model_slugstringmodel_slug is the resolved router model slug used to generate the recommendation (e.g. "claude-sonnet-4-6"). Surfaced so audit tooling can correlate recommendation drift with router changes.
generated_atgoogle.protobuf.Timestampgenerated_at is the UTC timestamp the recommendation was generated.

RecordConflictRequest

RecordConflictRequest is the internal RPC payload the governance Engine sends when cascade evaluation detects opposing layer verdicts.

FieldTypeLabelDescription
org_idstringorg_id is the tenant the conflict belongs to.
action_typestringaction_type is the governance action_type (e.g. 'tool_call').
targetstringtarget is the fine-grained target within action_type.
member_idstringmember_id is the member the cascade was evaluated against. Empty string is allowed for org-wide conflicts.
winning_policyConflictLayerwinning_policy describes the layer + policy that won the cascade.
losing_policyConflictLayerlosing_policy describes the layer + policy whose verdict was suppressed by sticky-deny / tightest-first precedence.
clearance_at_decisionint32clearance_at_decision is the numeric clearance captured at decision.
notified_partiesNotifiedPartyrepeatednotified_parties is the optional initial recipient set — when empty the service derives recipients from the policy authors + supervisors.

RecordConflictResponse

RecordConflictResponse returns the newly-persisted conflict row.

FieldTypeLabelDescription
conflictConflictconflict is the persisted row (with conflict_id set).

ResolveConflictRequest

ResolveConflictRequest finalises a conflict.

FieldTypeLabelDescription
conflict_idstringconflict_id is the UUID primary key.
actionResolveActionaction selects the resolution pathway.
notestringnote is a free-form human-readable annotation captured on the row.
side_idstringside_id MUST be set when action == RESOLVE_ACTION_SIDE_WITH and MUST match a Conflict.sides[].side_id (GAP-28, #1106). Ignored for all other actions.

ResolveConflictResponse

ResolveConflictResponse returns the terminal conflict state.

FieldTypeLabelDescription
conflictConflictconflict is the updated row state.

StreamConflictsRequest

StreamConflictsRequest subscribes to the lifecycle feed.

FieldTypeLabelDescription
include_backlogboolinclude_backlog replays currently-open conflicts the caller can see before streaming new events. Defaults to true for admin inboxes.

StreamConflictsResponse

StreamConflictsResponse is a single lifecycle event on the stream.

FieldTypeLabelDescription
conflictConflictconflict is the conflict row state at event time.
event_kindstringevent_kind is one of 'conflict.new', 'conflict.merge_ready', 'conflict.acknowledged', 'conflict.resolved'. Matches the pg_notify channel name minus the 'conflict_' prefix conversion.

ConflictStatus

ConflictStatus mirrors the policy_conflicts.status lifecycle enum.

NameNumberDescription
CONFLICT_STATUS_UNSPECIFIED0CONFLICT_STATUS_UNSPECIFIED is the zero value reserved by protobuf.
CONFLICT_STATUS_OPEN1CONFLICT_STATUS_OPEN is the initial state — freshly recorded.
CONFLICT_STATUS_ACKNOWLEDGED2CONFLICT_STATUS_ACKNOWLEDGED indicates a human has opened the conflict.
CONFLICT_STATUS_RESOLVED3CONFLICT_STATUS_RESOLVED indicates the conflict was actioned (merge accepted, override granted).
CONFLICT_STATUS_DISMISSED4CONFLICT_STATUS_DISMISSED is the soft-delete — dismissed without action.

ResolveAction

ResolveAction enumerates the resolution pathways ResolveConflict supports.

NameNumberDescription
RESOLVE_ACTION_UNSPECIFIED0RESOLVE_ACTION_UNSPECIFIED is the zero value reserved by protobuf.
RESOLVE_ACTION_ACCEPT_MERGE1RESOLVE_ACTION_ACCEPT_MERGE accepts the MergeSynthesisWorker's suggested_merge_json and marks the conflict resolved.
RESOLVE_ACTION_OVERRIDE2RESOLVE_ACTION_OVERRIDE grants an explicit override keeping the losing layer's verdict and marks the conflict resolved.
RESOLVE_ACTION_DISMISS3RESOLVE_ACTION_DISMISS dismisses the conflict without action.
RESOLVE_ACTION_ADOPT_RECOMMENDATION4RESOLVE_ACTION_ADOPT_RECOMMENDATION adopts the cached Recommendation (GAP-28, #1106) and marks the conflict resolved. Requires a cached recommendation to be present on the row; callers MUST run GetRecommendation first if absent.
RESOLVE_ACTION_SIDE_WITH5RESOLVE_ACTION_SIDE_WITH selects one of Conflict.sides[] as the resolution outcome (GAP-28, #1106) and marks the conflict resolved. Requires ResolveConflictRequest.side_id to be set to a side_id that exists on the conflict.

SideColor

SideColor is the rendering hint enum for ConflictSide. Mirrors the prototype's color field which is consumed via CSS variable lookup (var(--${color})).

NameNumberDescription
SIDE_COLOR_UNSPECIFIED0SIDE_COLOR_UNSPECIFIED is the zero value reserved by protobuf.
SIDE_COLOR_OK1SIDE_COLOR_OK indicates a permissive / approving side (green).
SIDE_COLOR_DANGER2SIDE_COLOR_DANGER indicates a blocking / hard-rule side (red).
SIDE_COLOR_WARN3SIDE_COLOR_WARN indicates a cautionary side (amber).
SIDE_COLOR_INFO4SIDE_COLOR_INFO indicates a neutral / informational side (blue).

ConflictService

ConflictService serves the conflict inbox + streaming + resolution lifecycle. See file-level doc for architectural context.

Method NameRequest TypeResponse TypeDescription
StreamConflictsStreamConflictsRequestStreamConflictsResponse streamStreamConflicts provides a server-streaming feed of conflict lifecycle events (created / merge_ready / acknowledged / resolved) scoped to the caller's tenant and member-level visibility.

The stream is backed by a LISTEN/NOTIFY consumer on the Postgres connection pool; the backend filters events to recipients derived from policy_conflicts.notified_parties. | | ListConflicts | ListConflictsRequest | ListConflictsResponse | ListConflicts returns a paginated conflict ledger for the admin inbox. Supports filters on status and creation window. | | GetConflict | GetConflictRequest | GetConflictResponse | GetConflict returns a single conflict with its full notification delivery ledger. | | AcknowledgeConflict | AcknowledgeConflictRequest | AcknowledgeConflictResponse | AcknowledgeConflict transitions status 'open' -> 'acknowledged'. Idempotent — re-acknowledging an already-acknowledged row is a no-op. | | ResolveConflict | ResolveConflictRequest | ResolveConflictResponse | ResolveConflict transitions status to 'resolved' or 'dismissed'. The Action enum distinguishes accept-merge / grant-override / dismiss. | | RecordConflict | RecordConflictRequest | RecordConflictResponse | RecordConflict is an internal RPC called by the governance Engine when cascade evaluation detects opposing non-default verdicts across layers. Returns the conflict_id which the Engine stamps onto Decision.ConflictID so the caller can render "see conflict #<id>".

This RPC is NOT RBAC-gated (no HTTP exposure); callers authenticate via the internal service mesh and provide org_id + member_id directly. | | GetRecommendation | GetRecommendationRequest | GetRecommendationResponse | GetRecommendation returns the platform's "Quad recommends:" verdict for a single conflict (GAP-27, #1105). The first call generates the recommendation by routing through the platform AI router with a structured prompt assembled from Conflict.sides[] + the tenant's recent comparable decisions; the result is cached on the conflict row so subsequent reads do NOT re-bill an LLM call.

RBAC: caller must be able to view the conflict (conflicts.view). |

Scalar Value Types

.proto TypeNotesC++JavaPythonGoC#PHPRuby
doubledoubledoublefloatfloat64doublefloatFloat
floatfloatfloatfloatfloat32floatfloatFloat
int32Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead.int32intintint32intintegerBignum or Fixnum (as required)
int64Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead.int64longint/longint64longinteger/stringBignum
uint32Uses variable-length encoding.uint32intint/longuint32uintintegerBignum or Fixnum (as required)
uint64Uses variable-length encoding.uint64longint/longuint64ulonginteger/stringBignum or Fixnum (as required)
sint32Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s.int32intintint32intintegerBignum or Fixnum (as required)
sint64Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s.int64longint/longint64longinteger/stringBignum
fixed32Always four bytes. More efficient than uint32 if values are often greater than 2^28.uint32intintuint32uintintegerBignum or Fixnum (as required)
fixed64Always eight bytes. More efficient than uint64 if values are often greater than 2^56.uint64longint/longuint64ulonginteger/stringBignum
sfixed32Always four bytes.int32intintint32intintegerBignum or Fixnum (as required)
sfixed64Always eight bytes.int64longint/longint64longinteger/stringBignum
boolboolbooleanbooleanboolboolbooleanTrueClass/FalseClass
stringA string must always contain UTF-8 encoded or 7-bit ASCII text.stringStringstr/unicodestringstringstringString (UTF-8)
bytesMay contain any arbitrary sequence of bytes.stringByteStringstr[]byteByteStringstringString (ASCII-8BIT)