approvals/v1: add auto-approve config and evidence types#56
Conversation
Add AutoApproveConfig message for controlling automatic approval behavior per workspace, AutoApproveEvidence for capturing the data behind auto decisions, DECISION_TYPE_AUTO_APPROVED enum value, approved_count to ApprovalHabit, and auto_approve_evidence to RequestApprovalResponse. Closes #55 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PR SummaryMedium Risk Overview Introduces Regenerates Go, Python, and TypeScript bindings to reflect the updated schema. Reviewed by Cursor Bugbot for commit 3d957fa. Bugbot is set up for automated code reviews on this repo. Configure here. |
ReviewClean proto changes. A few notes: Looks good
Heads-up: conflicts with #53PR #53 ( One thing to consider for a follow-upThe Downstream consumers that need updatingOnce merged, these services need proto vendor bumps and new case handling:
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Proto3 zero defaults undermine safety-critical threshold fields
- Marked the threshold and min_observations fields optional so consumers can detect omission and apply safe defaults instead of inheriting proto3 zero values.
Preview (c080deb7f5)
diff --git a/gen/go/approvals/v1/approvals.pb.go b/gen/go/approvals/v1/approvals.pb.go
--- a/gen/go/approvals/v1/approvals.pb.go
+++ b/gen/go/approvals/v1/approvals.pb.go
@@ -82,11 +82,12 @@
type DecisionType int32
const (
- DecisionType_DECISION_TYPE_UNSPECIFIED DecisionType = 0
- DecisionType_DECISION_TYPE_APPROVED DecisionType = 1
- DecisionType_DECISION_TYPE_DENIED DecisionType = 2
- DecisionType_DECISION_TYPE_ESCALATED DecisionType = 3
- DecisionType_DECISION_TYPE_EXPIRED DecisionType = 4
+ DecisionType_DECISION_TYPE_UNSPECIFIED DecisionType = 0
+ DecisionType_DECISION_TYPE_APPROVED DecisionType = 1
+ DecisionType_DECISION_TYPE_DENIED DecisionType = 2
+ DecisionType_DECISION_TYPE_ESCALATED DecisionType = 3
+ DecisionType_DECISION_TYPE_EXPIRED DecisionType = 4
+ DecisionType_DECISION_TYPE_AUTO_APPROVED DecisionType = 5
)
// Enum value maps for DecisionType.
@@ -97,13 +98,15 @@
2: "DECISION_TYPE_DENIED",
3: "DECISION_TYPE_ESCALATED",
4: "DECISION_TYPE_EXPIRED",
+ 5: "DECISION_TYPE_AUTO_APPROVED",
}
DecisionType_value = map[string]int32{
- "DECISION_TYPE_UNSPECIFIED": 0,
- "DECISION_TYPE_APPROVED": 1,
- "DECISION_TYPE_DENIED": 2,
- "DECISION_TYPE_ESCALATED": 3,
- "DECISION_TYPE_EXPIRED": 4,
+ "DECISION_TYPE_UNSPECIFIED": 0,
+ "DECISION_TYPE_APPROVED": 1,
+ "DECISION_TYPE_DENIED": 2,
+ "DECISION_TYPE_ESCALATED": 3,
+ "DECISION_TYPE_EXPIRED": 4,
+ "DECISION_TYPE_AUTO_APPROVED": 5,
}
)
@@ -187,6 +190,144 @@
return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{2}
}
+// AutoApproveConfig controls automatic approval behavior for a workspace.
+type AutoApproveConfig struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ Threshold *float32 `protobuf:"fixed32,2,opt,name=threshold,proto3,oneof" json:"threshold,omitempty"` // minimum confidence (default 0.95)
+ MinObservations *int32 `protobuf:"varint,3,opt,name=min_observations,json=minObservations,proto3,oneof" json:"min_observations,omitempty"` // minimum decisions before activation (default 20)
+ ExcludedRiskLevels []RiskLevel `protobuf:"varint,4,rep,packed,name=excluded_risk_levels,json=excludedRiskLevels,proto3,enum=approvals.v1.RiskLevel" json:"excluded_risk_levels,omitempty"` // always require human
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *AutoApproveConfig) Reset() {
+ *x = AutoApproveConfig{}
+ mi := &file_approvals_v1_approvals_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *AutoApproveConfig) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AutoApproveConfig) ProtoMessage() {}
+
+func (x *AutoApproveConfig) ProtoReflect() protoreflect.Message {
+ mi := &file_approvals_v1_approvals_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use AutoApproveConfig.ProtoReflect.Descriptor instead.
+func (*AutoApproveConfig) Descriptor() ([]byte, []int) {
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *AutoApproveConfig) GetEnabled() bool {
+ if x != nil {
+ return x.Enabled
+ }
+ return false
+}
+
+func (x *AutoApproveConfig) GetThreshold() float32 {
+ if x != nil && x.Threshold != nil {
+ return *x.Threshold
+ }
+ return 0
+}
+
+func (x *AutoApproveConfig) GetMinObservations() int32 {
+ if x != nil && x.MinObservations != nil {
+ return *x.MinObservations
+ }
+ return 0
+}
+
+func (x *AutoApproveConfig) GetExcludedRiskLevels() []RiskLevel {
+ if x != nil {
+ return x.ExcludedRiskLevels
+ }
+ return nil
+}
+
+// AutoApproveEvidence captures the data behind an automatic approval decision.
+type AutoApproveEvidence struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"`
+ Confidence float32 `protobuf:"fixed32,2,opt,name=confidence,proto3" json:"confidence,omitempty"`
+ ObservationCount int32 `protobuf:"varint,3,opt,name=observation_count,json=observationCount,proto3" json:"observation_count,omitempty"`
+ ThresholdApplied float32 `protobuf:"fixed32,4,opt,name=threshold_applied,json=thresholdApplied,proto3" json:"threshold_applied,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *AutoApproveEvidence) Reset() {
+ *x = AutoApproveEvidence{}
+ mi := &file_approvals_v1_approvals_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *AutoApproveEvidence) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AutoApproveEvidence) ProtoMessage() {}
+
+func (x *AutoApproveEvidence) ProtoReflect() protoreflect.Message {
+ mi := &file_approvals_v1_approvals_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use AutoApproveEvidence.ProtoReflect.Descriptor instead.
+func (*AutoApproveEvidence) Descriptor() ([]byte, []int) {
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *AutoApproveEvidence) GetPattern() string {
+ if x != nil {
+ return x.Pattern
+ }
+ return ""
+}
+
+func (x *AutoApproveEvidence) GetConfidence() float32 {
+ if x != nil {
+ return x.Confidence
+ }
+ return 0
+}
+
+func (x *AutoApproveEvidence) GetObservationCount() int32 {
+ if x != nil {
+ return x.ObservationCount
+ }
+ return 0
+}
+
+func (x *AutoApproveEvidence) GetThresholdApplied() float32 {
+ if x != nil {
+ return x.ThresholdApplied
+ }
+ return 0
+}
+
// ApprovalRequest is the canonical approval request record.
type ApprovalRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
@@ -207,7 +348,7 @@
func (x *ApprovalRequest) Reset() {
*x = ApprovalRequest{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[0]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -219,7 +360,7 @@
func (*ApprovalRequest) ProtoMessage() {}
func (x *ApprovalRequest) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[0]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -232,7 +373,7 @@
// Deprecated: Use ApprovalRequest.ProtoReflect.Descriptor instead.
func (*ApprovalRequest) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{0}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{2}
}
func (x *ApprovalRequest) GetId() string {
@@ -326,7 +467,7 @@
func (x *ApprovalRule) Reset() {
*x = ApprovalRule{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[1]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -338,7 +479,7 @@
func (*ApprovalRule) ProtoMessage() {}
func (x *ApprovalRule) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[1]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -351,7 +492,7 @@
// Deprecated: Use ApprovalRule.ProtoReflect.Descriptor instead.
func (*ApprovalRule) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{1}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{3}
}
func (x *ApprovalRule) GetId() string {
@@ -391,16 +532,17 @@
// ApprovalPolicy defines the set of rules for a workspace.
type ApprovalPolicy struct {
- state protoimpl.MessageState `protogen:"open.v1"`
- WorkspaceId string `protobuf:"bytes,1,opt,name=workspace_id,json=workspaceId,proto3" json:"workspace_id,omitempty"`
- Rules []*ApprovalRule `protobuf:"bytes,2,rep,name=rules,proto3" json:"rules,omitempty"`
- unknownFields protoimpl.UnknownFields
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ WorkspaceId string `protobuf:"bytes,1,opt,name=workspace_id,json=workspaceId,proto3" json:"workspace_id,omitempty"`
+ Rules []*ApprovalRule `protobuf:"bytes,2,rep,name=rules,proto3" json:"rules,omitempty"`
+ AutoApproveConfig *AutoApproveConfig `protobuf:"bytes,3,opt,name=auto_approve_config,json=autoApproveConfig,proto3" json:"auto_approve_config,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *ApprovalPolicy) Reset() {
*x = ApprovalPolicy{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[2]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -412,7 +554,7 @@
func (*ApprovalPolicy) ProtoMessage() {}
func (x *ApprovalPolicy) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[2]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -425,7 +567,7 @@
// Deprecated: Use ApprovalPolicy.ProtoReflect.Descriptor instead.
func (*ApprovalPolicy) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{2}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{4}
}
func (x *ApprovalPolicy) GetWorkspaceId() string {
@@ -442,6 +584,13 @@
return nil
}
+func (x *ApprovalPolicy) GetAutoApproveConfig() *AutoApproveConfig {
+ if x != nil {
+ return x.AutoApproveConfig
+ }
+ return nil
+}
+
// ApprovalDecision records a decision made on an approval request.
type ApprovalDecision struct {
state protoimpl.MessageState `protogen:"open.v1"`
@@ -457,7 +606,7 @@
func (x *ApprovalDecision) Reset() {
*x = ApprovalDecision{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[3]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -469,7 +618,7 @@
func (*ApprovalDecision) ProtoMessage() {}
func (x *ApprovalDecision) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[3]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -482,7 +631,7 @@
// Deprecated: Use ApprovalDecision.ProtoReflect.Descriptor instead.
func (*ApprovalDecision) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{3}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{5}
}
func (x *ApprovalDecision) GetId() string {
@@ -533,13 +682,14 @@
Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"`
AutoApproveConfidence float32 `protobuf:"fixed32,2,opt,name=auto_approve_confidence,json=autoApproveConfidence,proto3" json:"auto_approve_confidence,omitempty"`
ObservationCount int32 `protobuf:"varint,3,opt,name=observation_count,json=observationCount,proto3" json:"observation_count,omitempty"`
+ ApprovedCount int32 `protobuf:"varint,4,opt,name=approved_count,json=approvedCount,proto3" json:"approved_count,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ApprovalHabit) Reset() {
*x = ApprovalHabit{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[4]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -551,7 +701,7 @@
func (*ApprovalHabit) ProtoMessage() {}
func (x *ApprovalHabit) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[4]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -564,7 +714,7 @@
// Deprecated: Use ApprovalHabit.ProtoReflect.Descriptor instead.
func (*ApprovalHabit) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{4}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{6}
}
func (x *ApprovalHabit) GetPattern() string {
@@ -588,6 +738,13 @@
return 0
}
+func (x *ApprovalHabit) GetApprovedCount() int32 {
+ if x != nil {
+ return x.ApprovedCount
+ }
+ return 0
+}
+
type RequestApprovalRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
WorkspaceId string `protobuf:"bytes,1,opt,name=workspace_id,json=workspaceId,proto3" json:"workspace_id,omitempty"`
@@ -604,7 +761,7 @@
func (x *RequestApprovalRequest) Reset() {
*x = RequestApprovalRequest{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[5]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -616,7 +773,7 @@
func (*RequestApprovalRequest) ProtoMessage() {}
func (x *RequestApprovalRequest) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[5]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -629,7 +786,7 @@
// Deprecated: Use RequestApprovalRequest.ProtoReflect.Descriptor instead.
func (*RequestApprovalRequest) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{5}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{7}
}
func (x *RequestApprovalRequest) GetWorkspaceId() string {
@@ -689,15 +846,16 @@
}
type RequestApprovalResponse struct {
- state protoimpl.MessageState `protogen:"open.v1"`
- ApprovalRequest *ApprovalRequest `protobuf:"bytes,1,opt,name=approval_request,json=approvalRequest,proto3" json:"approval_request,omitempty"`
- unknownFields protoimpl.UnknownFields
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ ApprovalRequest *ApprovalRequest `protobuf:"bytes,1,opt,name=approval_request,json=approvalRequest,proto3" json:"approval_request,omitempty"`
+ AutoApproveEvidence *AutoApproveEvidence `protobuf:"bytes,2,opt,name=auto_approve_evidence,json=autoApproveEvidence,proto3" json:"auto_approve_evidence,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *RequestApprovalResponse) Reset() {
*x = RequestApprovalResponse{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[6]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -709,7 +867,7 @@
func (*RequestApprovalResponse) ProtoMessage() {}
func (x *RequestApprovalResponse) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[6]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -722,7 +880,7 @@
// Deprecated: Use RequestApprovalResponse.ProtoReflect.Descriptor instead.
func (*RequestApprovalResponse) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{6}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{8}
}
func (x *RequestApprovalResponse) GetApprovalRequest() *ApprovalRequest {
@@ -732,6 +890,13 @@
return nil
}
+func (x *RequestApprovalResponse) GetAutoApproveEvidence() *AutoApproveEvidence {
+ if x != nil {
+ return x.AutoApproveEvidence
+ }
+ return nil
+}
+
type ResolveApprovalRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
ApprovalRequestId string `protobuf:"bytes,1,opt,name=approval_request_id,json=approvalRequestId,proto3" json:"approval_request_id,omitempty"`
@@ -744,7 +909,7 @@
func (x *ResolveApprovalRequest) Reset() {
*x = ResolveApprovalRequest{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[7]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -756,7 +921,7 @@
func (*ResolveApprovalRequest) ProtoMessage() {}
func (x *ResolveApprovalRequest) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[7]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -769,7 +934,7 @@
// Deprecated: Use ResolveApprovalRequest.ProtoReflect.Descriptor instead.
func (*ResolveApprovalRequest) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{7}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{9}
}
func (x *ResolveApprovalRequest) GetApprovalRequestId() string {
@@ -809,7 +974,7 @@
func (x *ResolveApprovalResponse) Reset() {
*x = ResolveApprovalResponse{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[8]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -821,7 +986,7 @@
func (*ResolveApprovalResponse) ProtoMessage() {}
func (x *ResolveApprovalResponse) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[8]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -834,7 +999,7 @@
// Deprecated: Use ResolveApprovalResponse.ProtoReflect.Descriptor instead.
func (*ResolveApprovalResponse) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{8}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{10}
}
func (x *ResolveApprovalResponse) GetDecision() *ApprovalDecision {
@@ -853,7 +1018,7 @@
func (x *GetPolicyRequest) Reset() {
*x = GetPolicyRequest{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[9]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -865,7 +1030,7 @@
func (*GetPolicyRequest) ProtoMessage() {}
func (x *GetPolicyRequest) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[9]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[11]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -878,7 +1043,7 @@
// Deprecated: Use GetPolicyRequest.ProtoReflect.Descriptor instead.
func (*GetPolicyRequest) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{9}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{11}
}
func (x *GetPolicyRequest) GetWorkspaceId() string {
@@ -897,7 +1062,7 @@
func (x *GetPolicyResponse) Reset() {
*x = GetPolicyResponse{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[10]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -909,7 +1074,7 @@
func (*GetPolicyResponse) ProtoMessage() {}
func (x *GetPolicyResponse) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[10]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[12]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -922,7 +1087,7 @@
// Deprecated: Use GetPolicyResponse.ProtoReflect.Descriptor instead.
func (*GetPolicyResponse) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{10}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{12}
}
func (x *GetPolicyResponse) GetPolicy() *ApprovalPolicy {
@@ -941,7 +1106,7 @@
func (x *SetPolicyRequest) Reset() {
*x = SetPolicyRequest{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[11]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -953,7 +1118,7 @@
func (*SetPolicyRequest) ProtoMessage() {}
func (x *SetPolicyRequest) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[11]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[13]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -966,7 +1131,7 @@
// Deprecated: Use SetPolicyRequest.ProtoReflect.Descriptor instead.
func (*SetPolicyRequest) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{11}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{13}
}
func (x *SetPolicyRequest) GetPolicy() *ApprovalPolicy {
@@ -985,7 +1150,7 @@
func (x *SetPolicyResponse) Reset() {
*x = SetPolicyResponse{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[12]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -997,7 +1162,7 @@
func (*SetPolicyResponse) ProtoMessage() {}
func (x *SetPolicyResponse) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[12]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[14]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1010,7 +1175,7 @@
// Deprecated: Use SetPolicyResponse.ProtoReflect.Descriptor instead.
func (*SetPolicyResponse) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{12}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{14}
}
func (x *SetPolicyResponse) GetPolicy() *ApprovalPolicy {
@@ -1031,7 +1196,7 @@
func (x *ListPendingRequest) Reset() {
*x = ListPendingRequest{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[13]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1043,7 +1208,7 @@
func (*ListPendingRequest) ProtoMessage() {}
func (x *ListPendingRequest) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[13]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[15]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1056,7 +1221,7 @@
// Deprecated: Use ListPendingRequest.ProtoReflect.Descriptor instead.
func (*ListPendingRequest) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{13}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{15}
}
func (x *ListPendingRequest) GetWorkspaceId() string {
@@ -1090,7 +1255,7 @@
func (x *ListPendingResponse) Reset() {
*x = ListPendingResponse{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[14]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1102,7 +1267,7 @@
func (*ListPendingResponse) ProtoMessage() {}
func (x *ListPendingResponse) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[14]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[16]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1115,7 +1280,7 @@
// Deprecated: Use ListPendingResponse.ProtoReflect.Descriptor instead.
func (*ListPendingResponse) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{14}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{16}
}
func (x *ListPendingResponse) GetRequests() []*ApprovalRequest {
@@ -1141,7 +1306,7 @@
func (x *GetHabitsRequest) Reset() {
*x = GetHabitsRequest{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[15]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1153,7 +1318,7 @@
func (*GetHabitsRequest) ProtoMessage() {}
func (x *GetHabitsRequest) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[15]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[17]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1166,7 +1331,7 @@
// Deprecated: Use GetHabitsRequest.ProtoReflect.Descriptor instead.
func (*GetHabitsRequest) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{15}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{17}
}
func (x *GetHabitsRequest) GetWorkspaceId() string {
@@ -1185,7 +1350,7 @@
func (x *GetHabitsResponse) Reset() {
*x = GetHabitsResponse{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[16]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1197,7 +1362,7 @@
func (*GetHabitsResponse) ProtoMessage() {}
func (x *GetHabitsResponse) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[16]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[18]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1210,7 +1375,7 @@
// Deprecated: Use GetHabitsResponse.ProtoReflect.Descriptor instead.
func (*GetHabitsResponse) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{16}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{18}
}
func (x *GetHabitsResponse) GetHabits() []*ApprovalHabit {
@@ -1230,7 +1395,7 @@
func (x *EscalateRequest) Reset() {
*x = EscalateRequest{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[17]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1242,7 +1407,7 @@
func (*EscalateRequest) ProtoMessage() {}
func (x *EscalateRequest) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[17]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[19]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1255,7 +1420,7 @@
// Deprecated: Use EscalateRequest.ProtoReflect.Descriptor instead.
func (*EscalateRequest) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{17}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{19}
}
func (x *EscalateRequest) GetApprovalRequestId() string {
@@ -1281,7 +1446,7 @@
func (x *EscalateResponse) Reset() {
*x = EscalateResponse{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[18]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1293,7 +1458,7 @@
func (*EscalateResponse) ProtoMessage() {}
func (x *EscalateResponse) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[18]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[20]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1306,7 +1471,7 @@
// Deprecated: Use EscalateResponse.ProtoReflect.Descriptor instead.
func (*EscalateResponse) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{18}
+ return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{20}
}
func (x *EscalateResponse) GetApprovalRequest() *ApprovalRequest {
@@ -1326,7 +1491,7 @@
func (x *GetApprovalRequest) Reset() {
*x = GetApprovalRequest{}
- mi := &file_approvals_v1_approvals_proto_msgTypes[19]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[21]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1338,7 +1503,7 @@
func (*GetApprovalRequest) ProtoMessage() {}
func (x *GetApprovalRequest) ProtoReflect() protoreflect.Message {
- mi := &file_approvals_v1_approvals_proto_msgTypes[19]
+ mi := &file_approvals_v1_approvals_proto_msgTypes[21]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1351,7 +1516,7 @@
// Deprecated: Use GetApprovalRequest.ProtoReflect.Descriptor instead.
func (*GetApprovalRequest) Descriptor() ([]byte, []int) {
- return file_approvals_v1_approvals_proto_rawDescGZIP(), []int{19}
... diff truncated: showing 800 of 1583 linesYou can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit 96ceb21. Configure here.
…fety bypass Document safe defaults (0.95 threshold, 20 min_observations) in field comments so consumers know to nil-check and apply these when fields are unset. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Summary
AutoApproveConfigmessage to control per-workspace auto-approval (enabled flag, confidence threshold, min observations, excluded risk levels)AutoApproveEvidencemessage to capture the data behind an automatic approval decisionDECISION_TYPE_AUTO_APPROVED(value 5) toDecisionTypeenumauto_approve_configfield toApprovalPolicy(field 3)auto_approve_evidencefield toRequestApprovalResponse(field 2)approved_countfield toApprovalHabit(field 4)Closes #55
Test plan
buf lintpassesbuf generateregenerates Go/TS/Python bindings without errorsgo test ./...passes (3 test suites pass, generated code compiles cleanly)🤖 Generated with Claude Code